Solucionado Ajustar temperatura sin conectar Arduino a PC

Hola foro, quiero ajustar parámetros, que ahora ajusto modificando el código, con la computadora, tengo solo 2 pines digitales libres (7D y 11D), haciendo cambios en hardware podría liberar también (8D y 9D), seria un total de 4 pines pero e visto que se podría hacer via i2c con multiplexor y teclado 4x4, o multiplexor con botones, o un encoder otra posibilidad es usar los pines analógicos como digitales.

#include "max6675.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20 , 4);
// 0x27 0x3F , 0x20 , 0x38 ,  16 , 2 20 , 4

const byte CANTIDAD_CICLOS  = 2;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 39.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 36.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 40.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 42.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 29.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador
const unsigned long INTERVALO_ENTRE_LECTURAS = 2500UL;  // Milisegundos que han de transcurrir entre cada lectura de la temperatura
const bool FLECHAS = true;                              // Cambia las "animaciones" dependiedo de si se define true o false

// Definición de los pines usados
const byte THERMO_DO  = 6;  // Pines a los que está conectado el sensor de termperatura
const byte THERMO_CS  = 5;
const byte THERMO_CLK = 4;
const byte PIN_SELECTOR_TEMPERATURA = 10;   // Pin de selector de temperatura final (opción A o B)
const byte PIN_RELE_CALENTADOR      = 12;   // Pin al que está conectado el relé del calentador
const byte PIN_RELE_VENTILADOR      = 13;   // Pin al que está conectado el relé del ventilador

const byte VALOR_RELE_ENCENDIDO = LOW;                   // Valor que ha de tener el pin para que se active el relé
const byte VALOR_RELE_APAGADO   = !VALOR_RELE_ENCENDIDO; // Esta definición no hay que tocarla nunca, se ha de modificar la de VALOR_RELE_ENCENDIDO si fuera necesario

const char CARACTER_CALENTAR = 0; // Carácter que se usa para la animación que indica que se está calentando
const char CARACTER_ENFRIAR = 1;  // Carácter que se usa para la animación que indica que se está enfriando

// Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
const byte vccPin     = 3;
const byte gndPin     = 2;
const byte gndPinf    = 8;
const byte vccPinr    = 9;

MAX6675 thermocouple(THERMO_CLK, THERMO_CS, THERMO_DO);
float temperatura = 0;                          // Almacena el valor de la temperatura
bool temperaturaErronea = false;                // Se pone a true si se detecta una temperatura anómala
unsigned long instanteAnterior = 0;             // Para controlar las pausas en la máquina de estados
unsigned long instanteAnteriorTemperatura = 0;  // Para controlar el tiempo transcurrido desde la última lectura de temperatura
unsigned long instanteAnteriorCaracter = 0;     // Para controlar el tiempo de la animación de los caractéres
byte ciclo = 0;                                 // Lleva la cuenta del ciclo en que se encuentra
byte pasoCaracter = 0;                          // Para controlar en que paso está de la animación de los caractéres (todos tienen cuato pasos)
int temperaturaCalentamientoFinal = 0;          // Almacena el valor de la temperatura del calentamiento final que vale según la posición del delector de temperatura
int temperaturaCalentamientoFinalAnterior = 0;  // Tan solo es para ver si ha cambiado el valor de temperaturaCalentamientoFinal. Si es así, actualiza su visualización en el LCD

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados "principal"
enum estado_t {
  ESTADO_INICIAL,                 // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
  ESTADO_PRESENTACION,            // Espera que transcurra el tiempo de presentación para luego pasar a calentar
  ESTADO_CALENTANDO_CICLO,        // Está calentando durante uno de los ciclos intermedios
  ESTADO_ENFRIANDO_CICLO,         // Está enfriando durante uno de los ciclos intermedios
  ESTADO_CALENTANDO_FINAL,        // Está en el calentantamiento del final
  ESTADO_ENFRIANDO_FINAL,         // Está en el enfriado final
  ESTADO_ERROR_TEMPERATURA,       // Se ha detectado un error en el sensor de temperatura
  ESTADO_PAUSA_ERROR_TEMPERATURA, // Espera a que se quite el error del sensor de temperatura
  ESTADO_FINAL                    // Ha finalizado todo el proceso
};
estado_t estado = ESTADO_INICIAL;         // Variable en la que se guarda el estado de la máquina de estado principal
estado_t estadoGuardado = ESTADO_INICIAL; // Guarda el estado en que estaba cuando se ha producido el error de temperatura, para volver a ese estado cuando se subsane el error

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados que controla la visualizacón del LCD
enum estado_pantalla_t {
  ESTADO_LCD_OCIOSO,              // No ha de hacer nada con el LCD, más que dejarlo tal cual está
  ESTADO_LCD_PRESENTACION,        // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
  ESTADO_LCD_PREPARAR,            // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
  ESTADO_LCD_TRABAJANDO,          // Muestra y actualiza la información
  ESTADO_LCD_TEMPERATURA,         // Muestra actualizado el valor de la temperatura
  ESTADO_LCD_MUESTRA_FINAL        // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
} estadoLCD = ESTADO_LCD_OCIOSO;  // Variable en la que se guarda el estado de la máquina de estados del LCD

// Definiciones de los caractéres para la animación
byte cararterSubir[4][8] = {  // Las flechas que suben
  {
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001
  }
  , {
    B00100,
    B01010,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000
  }
  , {
    B00000,
      B00000,
      B00100,
      B01110,
      B11011,
      B10001,
      B00000,
      B00000
  }
  , {
    B00000,
      B00100,
      B01110,
      B11011,
      B10001,
      B00100,
      B01010,
      B10001
  }
};

byte cararterBajar[4][8] = {  // Las flechas que bajan
  {
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100
  }
  , {
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B01010,
    B00100
  }
  , {
    B00000,
      B00000,
      B10001,
      B11011,
      B01110,
      B00100,
      B00000,
      B00000
  }
  , {
    B10001,
      B01010,
      B00100,
      B10001,
      B11011,
      B01110,
      B00100,
      B00000
  }
};

byte cararterCalentar[4][8] = { // Calentando
  {
    B00001,
    B01001,
    B10010,
    B10010,
    B01001,
    B01001,
    B10100,
    B01110
  }
  , {
    B01000,
    B10010,
    B10010,
    B01001,
    B01001,
    B10010,
    B00110,
    B01110
  }
  , {
    B00010,
      B10010,
      B01001,
      B01001,
      B10010,
      B10010,
      B01100,
      B01110
  }
  , {
    B10000,
      B01001,
      B01001,
      B10010,
      B10010,
      B01001,
      B00101,
      B01110
  }
};

byte cararterEnfriar[4][8] = {  // Enfriando
  {
    B00000,
    B00110,
    B11001,
    B00000,
    B10011,
    B01100,
    B00100,
    B01110
  }
  , {
    B00000,
    B10011,
    B01100,
    B00000,
    B11001,
    B00110,
    B00100,
    B01110
  }
  , {
    B00000,
      B11001,
      B00110,
      B00000,
      B01100,
      B10011,
      B00100,
      B01110
  }
  , {
    B00000,
      B01100,
      B10011,
      B00000,
      B00110,
      B11001,
      B00100,
      B01110
  }
};

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(PIN_RELE_CALENTADOR, OUTPUT);
  digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Inicialmente el calentador está apagado
  pinMode(PIN_RELE_VENTILADOR, OUTPUT);
  digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Inicialmente el ventilador está apagado
  pinMode(PIN_SELECTOR_TEMPERATURA, INPUT);
  
  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(vccPin, OUTPUT);
  digitalWrite(vccPin, HIGH);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(gndPin, OUTPUT);
  digitalWrite(gndPin, LOW);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(vccPinr, OUTPUT);
  digitalWrite(vccPinr, HIGH);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(gndPinf, OUTPUT);
  digitalWrite(gndPinf, LOW);
}

void loop() {
  unsigned long instanteActual = millis();                      // Tomamos el instante actual, para controlar los tiempos
  // Animación de los caracteres animados
  if ((instanteActual - instanteAnteriorCaracter) >= 250UL) {   // Cada 250 milisegundos redefinimos los caractéres
    instanteAnteriorCaracter = instanteActual;
    pasoCaracter = (pasoCaracter + 1) % 4;                      // Cada vez que pasa por aquí, pasoCaracter de incrementa en uno volviendo a cero despues de valer 3
    if (FLECHAS) {  // Si se ha definido FLECHAS como true, animará unas flechas ascendentes y descendentes
      lcd.createChar(CARACTER_CALENTAR, cararterSubir[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterBajar[pasoCaracter]);
    }
    else {          // Si se ha definido FLECHAS como false, animará "otra cosas"
      lcd.createChar(CARACTER_CALENTAR, cararterCalentar[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterEnfriar[pasoCaracter]);
    }
  }
  // Lectura de la temperatura cada cierto intervalo y control de si se sale del rango "válido" (de -99º a 200º)
  if ((instanteActual - instanteAnteriorTemperatura) >= INTERVALO_ENTRE_LECTURAS) {
    instanteAnteriorTemperatura = instanteActual;
    temperatura = thermocouple.readCelsius();
    temperaturaErronea = !((temperatura < 200) && (temperatura > -99));
  }
  // Control del commutador que selecciona la temperatura máxima final
  temperaturaCalentamientoFinal = (digitalRead(PIN_SELECTOR_TEMPERATURA) ? MAXIMO_CALENTANDO_FINAL_A : MAXIMO_CALENTANDO_FINAL_B);
  if (temperaturaCalentamientoFinal != temperaturaCalentamientoFinalAnterior) {
    temperaturaCalentamientoFinalAnterior = temperaturaCalentamientoFinal;
  }
  // Control de la máquina de estados "principal"
  switch (estado) {
    case ESTADO_INICIAL :
      // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
      instanteAnterior = instanteActual;    // Nos quedamos con el instante en que empieza a contar el tiempo
      estado = ESTADO_PRESENTACION;         // Pasamos a esperar a que termine el tiempo de la presentación
      estadoLCD = ESTADO_LCD_PRESENTACION;  // Cambia de estado la máquina de estados del LCD para que muestre la presentación
      break;
    case ESTADO_PRESENTACION :
      // Espera que transcurra el tiempo de presentación para luego pasar a calentar
      if ((instanteActual - instanteAnterior) >= 3000UL) {        // Si ha transcurrido el tiempo deseado
        estadoLCD = ESTADO_LCD_PREPARAR;                          // Le dice a la máquina de estados del LCD que muestre los datos "estáticos"
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador
        if (CANTIDAD_CICLOS == 0) {
          estado = ESTADO_CALENTANDO_FINAL;   // Si no hay ciclos intermedios, entonces pasa directamente al calentamiento final
        }
        else {                                // Si hay ciclos
          ciclo = 1;                          // Está en el primer ciclo
          estado = ESTADO_CALENTANDO_CICLO;   // Pasa a calentar un ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_CICLO :
      // Está calentando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= MAXIMO_CALENTANDO) {                   // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_CICLO;                        // Cambia al estado de enfriamiento del ciclo
      }
      break;
    case ESTADO_ENFRIANDO_CICLO :
      // Está enfriando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO) {                      // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        if (ciclo >= CANTIDAD_CICLOS) {                           // Si estaba en el último ciclo
          estado = ESTADO_CALENTANDO_FINAL;                       // Se pasa a el calentamiento final
        }
        else {                                                    // Si no estaba en el último ciclo
          ciclo++;                                                // Se incrementa el contador de ciclos (el famoso for)
          estado = ESTADO_CALENTANDO_CICLO;                       // Se vuleve a calentar del ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_FINAL :
      // Está en el calentantamiento del final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= temperaturaCalentamientoFinal) {       // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_FINAL;                        // Pasa al enfriamiento final
      }
      break;
    case ESTADO_ENFRIANDO_FINAL :
      // Está en el enfriado final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO_FINAL) {              // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Apaga el ventilador
        estado = ESTADO_FINAL;                                  // Cambia al estado de finalización del proceso
      }
      break;
    case ESTADO_ERROR_TEMPERATURA :
      // Se ha detectado un error en el sensor de temperatura
      // Nos aseguramos de que se apaga el calentador y el ventilador
      digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);    // Apaga el calentador
      digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);    // Apaga el ventilador
      estado = ESTADO_PAUSA_ERROR_TEMPERATURA;                  // Pasa a esperar a que vuelva a haber una lectura correcta de la temperatura
      break;
    case ESTADO_PAUSA_ERROR_TEMPERATURA :
      // Espera a que se quite el error del sensor de temperatura
      if (!temperaturaErronea) {          // Si la temperatura no es errónea (es correcta)
        estado = estadoGuardado;          // Recupera el estado guardado de la máquina de estado
        switch (estado) {
          case ESTADO_CALENTANDO_CICLO :
          case ESTADO_CALENTANDO_FINAL :
            digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador sólo para los estados ESTADO_CALENTANDO_CICLO y ESTADO_CALENTANDO_FINAL
          case ESTADO_ENFRIANDO_CICLO :
          case ESTADO_ENFRIANDO_FINAL :
            digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador para los cuatro estados
            break;
          default :
            // No hace enada para todos los demás estados
            break;
        }
      }
      break;
    case ESTADO_FINAL :
      // Ha finalizado todo el proceso
      // No hace nada
      break;
  }

  // Control de lo que se muestra en el LCD ayudado por una máquina de estados propia
  // .. y consultando el estado de la máquina de estado principal
  switch (estadoLCD) {
    case ESTADO_LCD_OCIOSO :
      // No ha de hacer nada con el LCD, más que dejarlo tal cual está
      // No hace nada
      break;
    case ESTADO_LCD_PRESENTACION :
      // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("D. Marmo Servicios")); // Mensaje a desplegar
      lcd.setCursor(0, 2);
      lcd.print(F("    Secadora Do 6 ")); // Mensaje 2 a desplegar
      estadoLCD = ESTADO_LCD_OCIOSO;      // Pasa a no hacer nada, hasta que la máquina de estados principal le diga que haga algo
      break;
    case ESTADO_LCD_PREPARAR :
      // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
      lcd.clear();
      if (CANTIDAD_CICLOS != 0) {
        // Si hay ciclos, se muestra las temperaturas de los ciclos
        lcd.setCursor(1, 2);
        lcd.print(MAXIMO_CALENTANDO);
        lcd.write(223);
        lcd.print('/');
        lcd.print(MINIMO_ENFRIANDO);
        lcd.write(223);
      }
      // Se muestran las temperaturas finales
      lcd.setCursor(1, 3);
      lcd.print(temperaturaCalentamientoFinal); // El valor de esta temperatura se ha calculado según el conmutador selector
      lcd.write(223);
      lcd.print('/');
      lcd.print(MINIMO_ENFRIANDO_FINAL);
      lcd.write(223);
      estadoLCD = ESTADO_LCD_TRABAJANDO;        // Pasa al estado de "trabajo", donde muestra el resto de valores
      // No ponemos break ya que queremos que realice las tareas del estado ESTADO_LCD_TRABAJANDO que ha de tratarse a continuación
    case ESTADO_LCD_TRABAJANDO :
      // Muestra y actualiza la información
      { // Creamos un bloque de código dentro de este case porque las siguientes variables sólo van a ser usadas en este case
        bool mostrarPasos = false;    // Tan sólo es para saber si ha de actualizar por qué paso va, ya que está dentro de un ciclo
        int temperaturaDestino = -1;  // Temperatura que se quiere alcanzar. Inicializada a -1 por si no se le asigna valor, que tenga algo que mostrar
        char subeBaja = ' ';          // Carárter animado que indica si se está calentando o enfriando
        char calientaCiclo = ' ';     // Carárter animado que se muestra junto a la temperatura máxima del ciclo
        char enfriaCiclo = ' ';       // Carárter animado que se muestra junto a la temperatura mínima del ciclo
        char calientaFinal = ' ';     // Carárter animado que se muestra junto a la temperatura máxima final
        char enfriaFinal = ' ';       // Carárter animado que se muestra junto a la temperatura mínima final
        lcd.setCursor(1, 0);          // Posiciona el cursor para poner qué es lo que está haciendo
        // Aprovechamos la máquina de estados «principal» para saber qué hemos de mostrar
        switch (estado) {
          case ESTADO_INICIAL :
          case ESTADO_PRESENTACION :
            // No hace nada. Es más, no debería de darse nunca esta condición
            break;
          case ESTADO_CALENTANDO_CICLO :
            lcd.print(F("CALENTANDO"));             // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;           // Animación de calentar
            calientaCiclo = CARACTER_CALENTAR;      // Animación de calentar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MAXIMO_CALENTANDO; // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ENFRIANDO_CICLO :
            lcd.print(F("ENFRIANDO "));             // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;            // Animación de enfriar
            enfriaCiclo = CARACTER_ENFRIAR;         // Animación de enfriar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MINIMO_ENFRIANDO;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_CALENTANDO_FINAL :
            lcd.print(F("CALENTANDO"));                         // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;                       // Animación de calentar
            calientaFinal = CARACTER_CALENTAR;                  // Animación de calentar
            temperaturaDestino = temperaturaCalentamientoFinal; // Temperatura que se ha de alcanzar, que depende el conmutador de selección
            break;
          case ESTADO_ENFRIANDO_FINAL :
            lcd.print(F("ENFRIANDO "));                   // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;                  // Animación de enfriar
            enfriaFinal = CARACTER_ENFRIAR;               // Animación de enfriar
            temperaturaDestino = MINIMO_ENFRIANDO_FINAL;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ERROR_TEMPERATURA :       // Tanto en el estado ESTADO_ERROR_TEMPERATURA...
          case ESTADO_PAUSA_ERROR_TEMPERATURA : // ... como en el estado ESTADO_PAUSA_ERROR_TEMPERATURA
            if ((instanteActual / 500) & 1) {   // Se muestra parpadeando el mensaje de aviso
              lcd.print(F("SENSOR MAL"));
            }
            else {
              lcd.print(F("          "));
            }
            // Se va a mostrar la temperatura destino dependiendo del estado en que estaba la máquina de estado principal cuando ha fallado la temperatura
            switch (estadoGuardado) {
              case ESTADO_CALENTANDO_CICLO :
                temperaturaDestino = MAXIMO_CALENTANDO;
                break;
              case ESTADO_CALENTANDO_FINAL :
                temperaturaDestino = temperaturaCalentamientoFinal;
                break;
              case ESTADO_ENFRIANDO_CICLO :
                temperaturaDestino = MINIMO_ENFRIANDO;
                break;
              case ESTADO_ENFRIANDO_FINAL :
                temperaturaDestino = MINIMO_ENFRIANDO_FINAL;
              default :
                break;
            }
            break;
          case ESTADO_FINAL :
            // La máquina de estados principal ha finalizado
            estadoLCD = ESTADO_LCD_MUESTRA_FINAL; // La máquina de estados del LCD pasa a mostrar el mensaje final
            break;
        }
        // Muestra por qué ciclo va
        if (mostrarPasos) {
          lcd.setCursor(3 - (ciclo / 10), 1);
          lcd.print(ciclo);
          lcd.print('/');
          lcd.print(CANTIDAD_CICLOS);
          lcd.print('x');
        }
        // Muestra la temperatura destino
        lcd.setCursor(14, 1);
        lcd.print(temperaturaDestino);
        lcd.write(223);
        lcd.write(subeBaja);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(0, 2);
        lcd.write(calientaCiclo);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 2);
        lcd.write(enfriaCiclo);
        // Muestra la temperatura máxima final (que puede variar)
        lcd.setCursor(0, 3);
        lcd.write(calientaFinal);
        lcd.print(temperaturaCalentamientoFinal);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 3);
        lcd.write(enfriaFinal);
      }
    case ESTADO_LCD_TEMPERATURA :
      // Muestra actualizado el valor de la temperatura
      lcd.setCursor(13, 0);
      if (temperaturaErronea) {
        lcd.print(F(" --.--")); // Es lo que muestra si la lectura de temperatura no es correcta
      }
      else {
        if ((temperatura < 100) && (temperatura > -10)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        if ((temperatura < 10) && (temperatura >= 0)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        lcd.print(temperatura);
      }
      lcd.write(223);
      break;
    case ESTADO_LCD_MUESTRA_FINAL :
      // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
      lcd.clear();
      lcd.setCursor(3, 2);
      lcd.print(F("FIN DE PROCESO"));
      estadoLCD = ESTADO_LCD_TEMPERATURA;
      break;
  }
}

Saludos.

Cuento con los siguientes componentes,

Hola,este encoder cuenta con resistencias pull-up integradas y un par de capacitores lo que lo hace muy estable, también tengo un multiplexor HP4067, teclado de membrana 4x4 y 1x4 el 4x4 se puede conectar a un PCF8574 envia la información vía i2c, el 1x4 parece que no es compatible con PCF8574, pero si es compatible con HP4067, que a su vez es compatible con botones, no tengo la mas remota idea, de como se implementa un menú, con submenús, son 6 ajustes diferentes, el codigo lo creo IgnoranteAbsoluto es muy complejo para mi e intentado hacer pequeñas modificaciones todas infructuosas.

const byte CANTIDAD_CICLOS  = 12;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 58.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 48.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 60.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 70.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 36.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador

estos son los 6 ajustes que quiero hacer, ya sea con botones, teclado, encoder con lo ustedes me recomienden que sea mas seguro por tratarse de equipos que funcionan con gas LP.
Saludos.

Hola haciendo pruebas con teclado 1x4 conectando lo a los pines analógicos veo que si funciona con el siguiente código.


#define key4 A2 //connect wire 1 to pin 2
#define key3 A3 //connect wire 2 to pin 3
#define key2 A4 //connect wire 3 to pin 4
#define key1 A5 //connect wire 4 to pin 5

void setup() {
 Serial.begin(9600);
 pinMode(key1, INPUT_PULLUP);// set pin as input
 pinMode(key2, INPUT_PULLUP);// set pin as input
 pinMode(key3, INPUT_PULLUP);// set pin as input
 pinMode(key4, INPUT_PULLUP);// set pin as input   
}

void loop() {
  int key2S = digitalRead(key1);
  int key1S = digitalRead(key2);
  int key4S = digitalRead(key3);
  int key3S = digitalRead(key4); 
 // Code written for Robojax.com video Tutorial
     if(!key1S){
      Serial.println("key 1 is pressed");
     }
     if(!key2S){
      Serial.println("key 2 is pressed");
     }
     if(!key3S){
      Serial.println("key 3 is pressed");
     }
     if(!key4S){
      Serial.println("key 4 is pressed");
     }          
     delay(100);
}

Saludos
Nota: intentare hacer lo mismo con un encoder rotativo

¿Donde está la duda? Simplemente nos estas mostrando y dando las soluciones. ¿Qué te podemos decir?

No hay problema en usar los pines analógicos como digitales. Así que si tienes los pines A0 a A3 libres puedes usarlos sin problemas.

Es más, con un solo pin analógico puedes realizar una botonera con multiples botones: ejemplo.

Lo del encoder dependerá de la libreria que uses. Lo mejor es una que utilice interrupciones en el pin (PIN CHANGE INTERRUPT) para obtener buenos resultados, aunque dependiendo el modelo de Arduino esa habilidad la disponen unos u otros pines.

Lo que si descarto es el keypad, puesto que para hacerlo funcionar necesitaras 8 pines o como bien dices usar un expansor i2c.

En cuanto a lo del menu... eso es un mundo... existen cientos de librerias para hacer menús. Dependerá de la complejidad que tenga, aunque lo mas fácil es montar una maquina de estados

Hola @victorjam, si el keypad 4x4 son demasiadas teclas para un menu que creo que se puede hacer con tres botones el mayor de mis problemas es que no tengo la menor idea de como hacer un menu con 6 sub-menus

const byte CANTIDAD_CICLOS  = 12;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 58.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 48.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 60.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 70.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 36.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador

la opción del encoder me resulta la mas adecuada, para ser manejada por gente sin experiencia, la idea de los botones es porque tengo casi todos los pines analógicos libres, y los digitales como esta ahora el código solo tengo uno libre, modificando el hardware podría tener tres libres, para implementar el menu y sus submenus el codigo es una maquina de estados, diseñado por @IgnoranteAbsoluto he intentado hacer le algunos cambios infructuosos, por lo complejo. Funciona muy bien pero hay días que tengo que hacer le cambios al código hasta 8 veces cosa que ahora que estuve enferma, complico mucho el funcionamiento de la deshidratadora, por eso la necesidad de implementar el menú y sub-menús.

teclsdo membrana 1x4

A esto no le llamaría un keypad, en realidad es una botonera de membrana y funciona bien en los pines analógicos se me hace una buena opción también.
Nota: si me puedes dar una idea de como implementar los menús seria de gran ayuda para iniciarme en el tema.
Saludos, @victorjam

Hola foro, ya empece a integrar el encoder, hasta el void setup copila, en cuanto al void loop(){ ya no consigo que copile.

#include "max6675.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20 , 4);
// 0x27 0x3F , 0x20 , 0x38 ,  16 , 2 20 , 4

#define DT  8       // DT a pin digital 2 (DT en modulo) Pin de la interrupcion 
#define CLK 9       // CLK a pin digital 3 (CLK en modulo)
#define btn 10       // Swiche del encoder al pin 4


const byte CANTIDAD_CICLOS  = 2;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 39.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 36.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 40.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 42.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 29.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador

char *menu1[6] = {"CANTIDAD_CICLOS", " MAXIMO_CALENTANDO ", " MINIMO_ENFRIANDO", "MAXIMO_CALENTANDO_FINAL_A", "MAXIMO_CALENTANDO_FINAL_B", "MINIMO_ENFRIANDO_FINAL"};
volatile int posicion_encoder = 0; // Posicion del encoder
int ant_posicion = 0;    // Valor anterior de la posicion del
const unsigned long INTERVALO_ENTRE_LECTURAS = 2500UL;  // Milisegundos que han de transcurrir entre cada lectura de la temperatura
const bool FLECHAS = true;                              // Cambia las "animaciones" dependiedo de si se define true o false

// Definición de los pines usados
const byte THERMO_DO  = 6;  // Pines a los que está conectado el sensor de termperatura
const byte THERMO_CS  = 5;
const byte THERMO_CLK = 4;
const byte PIN_SELECTOR_TEMPERATURA = 11;   // Pin de selector de temperatura final (opción A o B)
const byte PIN_RELE_CALENTADOR      = 12;   // Pin al que está conectado el relé del calentador
const byte PIN_RELE_VENTILADOR      = 13;   // Pin al que está conectado el relé del ventilador

const byte VALOR_RELE_ENCENDIDO = LOW;                   // Valor que ha de tener el pin para que se active el relé
const byte VALOR_RELE_APAGADO   = !VALOR_RELE_ENCENDIDO; // Esta definición no hay que tocarla nunca, se ha de modificar la de VALOR_RELE_ENCENDIDO si fuera necesario

const char CARACTER_CALENTAR = 0; // Carácter que se usa para la animación que indica que se está calentando
const char CARACTER_ENFRIAR = 1;  // Carácter que se usa para la animación que indica que se está enfriando

// Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
const byte vccPin     = 3;
const byte gndPin     = 2;

MAX6675 thermocouple(THERMO_CLK, THERMO_CS, THERMO_DO);
float temperatura = 0;                          // Almacena el valor de la temperatura
bool temperaturaErronea = false;                // Se pone a true si se detecta una temperatura anómala
unsigned long instanteAnterior = 0;             // Para controlar las pausas en la máquina de estados
unsigned long instanteAnteriorTemperatura = 0;  // Para controlar el tiempo transcurrido desde la última lectura de temperatura
unsigned long instanteAnteriorCaracter = 0;     // Para controlar el tiempo de la animación de los caractéres
byte ciclo = 0;                                 // Lleva la cuenta del ciclo en que se encuentra
byte pasoCaracter = 0;                          // Para controlar en que paso está de la animación de los caractéres (todos tienen cuato pasos)
int temperaturaCalentamientoFinal = 0;          // Almacena el valor de la temperatura del calentamiento final que vale según la posición del delector de temperatura
int temperaturaCalentamientoFinalAnterior = 0;  // Tan solo es para ver si ha cambiado el valor de temperaturaCalentamientoFinal. Si es así, actualiza su visualización en el LCD
int encoder;
// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados "principal"
enum estado_t {
  ESTADO_INICIAL,                 // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
  ESTADO_PRESENTACION,            // Espera que transcurra el tiempo de presentación para luego pasar a calentar
  ESTADO_CALENTANDO_CICLO,        // Está calentando durante uno de los ciclos intermedios
  ESTADO_ENFRIANDO_CICLO,         // Está enfriando durante uno de los ciclos intermedios
  ESTADO_CALENTANDO_FINAL,        // Está en el calentantamiento del final
  ESTADO_ENFRIANDO_FINAL,         // Está en el enfriado final
  ESTADO_ERROR_TEMPERATURA,       // Se ha detectado un error en el sensor de temperatura
  ESTADO_PAUSA_ERROR_TEMPERATURA, // Espera a que se quite el error del sensor de temperatura
  ESTADO_FINAL                    // Ha finalizado todo el proceso
};
estado_t estado = ESTADO_INICIAL;         // Variable en la que se guarda el estado de la máquina de estado principal
estado_t estadoGuardado = ESTADO_INICIAL; // Guarda el estado en que estaba cuando se ha producido el error de temperatura, para volver a ese estado cuando se subsane el error

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados que controla la visualizacón del LCD
enum estado_pantalla_t {
  ESTADO_LCD_OCIOSO,              // No ha de hacer nada con el LCD, más que dejarlo tal cual está
  ESTADO_LCD_PRESENTACION,        // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
  ESTADO_LCD_PREPARAR,            // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
  ESTADO_LCD_TRABAJANDO,          // Muestra y actualiza la información
  ESTADO_LCD_TEMPERATURA,         // Muestra actualizado el valor de la temperatura
  ESTADO_LCD_MUESTRA_FINAL        // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
} estadoLCD = ESTADO_LCD_OCIOSO;  // Variable en la que se guarda el estado de la máquina de estados del LCD

// Definiciones de los caractéres para la animación
byte cararterSubir[4][8] = {  // Las flechas que suben
  {
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001
  }
  , {
    B00100,
    B01010,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000
  }
  , {
    B00000,
    B00000,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000,
    B00000
  }
  , {
    B00000,
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01010,
    B10001
  }
};

byte cararterBajar[4][8] = {  // Las flechas que bajan
  {
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100
  }
  , {
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B01010,
    B00100
  }
  , {
    B00000,
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B00000,
    B00000
  }
  , {
    B10001,
    B01010,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100,
    B00000
  }
};

byte cararterCalentar[4][8] = { // Calentando
  {
    B00001,
    B01001,
    B10010,
    B10010,
    B01001,
    B01001,
    B10100,
    B01110
  }
  , {
    B01000,
    B10010,
    B10010,
    B01001,
    B01001,
    B10010,
    B00110,
    B01110
  }
  , {
    B00010,
    B10010,
    B01001,
    B01001,
    B10010,
    B10010,
    B01100,
    B01110
  }
  , {
    B10000,
    B01001,
    B01001,
    B10010,
    B10010,
    B01001,
    B00101,
    B01110
  }
};

byte cararterEnfriar[4][8] = {  // Enfriando
  {
    B00000,
    B00110,
    B11001,
    B00000,
    B10011,
    B01100,
    B00100,
    B01110
  }
  , {
    B00000,
    B10011,
    B01100,
    B00000,
    B11001,
    B00110,
    B00100,
    B01110
  }
  , {
    B00000,
    B11001,
    B00110,
    B00000,
    B01100,
    B10011,
    B00100,
    B01110
  }
  , {
    B00000,
    B01100,
    B10011,
    B00000,
    B00110,
    B11001,
    B00100,
    B01110
  }
};

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(PIN_RELE_CALENTADOR, OUTPUT);
  digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Inicialmente el calentador está apagado
  pinMode(PIN_RELE_VENTILADOR, OUTPUT);
  digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Inicialmente el ventilador está apagado
  pinMode(PIN_SELECTOR_TEMPERATURA, INPUT);
  pinMode(DT, INPUT);
  pinMode(CLK, INPUT);
  pinMode(btn, INPUT_PULLUP);    // Entrada con resistencia Pullup para el Switch del encoder

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(vccPin, OUTPUT);
  digitalWrite(vccPin, HIGH);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(gndPin, OUTPUT);
  digitalWrite(gndPin, LOW);

  attachInterrupt(digitalPinToInterrupt(DT), encoder, LOW);  // Se habilita interrupcion en pin 2 modo LOW
  Serial.println("SENSOR ROTACION");  // Mensaje inicial en Puerto Serie
  lcd.setCursor(0, 1);
  lcd.print(menu1[posicion_encoder]);
}

void loop() {
 /* 
 if (digitalRead(btn) == LOW)     
    {
    if (!bucle && digitalRead(btn) >) {
    for (int i = 0; i < 1; i++) {      
      accion1();                          
                
      if (digitalRead(btn) == HIGH)    
    {
      if (!bucle && digitalRead(btn) <) {
    for (int i = 0; i < 1; i--) {
    }
    */
  unsigned long instanteActual = millis();                      // Tomamos el instante actual, para controlar los tiempos
  // Animación de los caracteres animados
  if ((instanteActual - instanteAnteriorCaracter) >= 250UL) {   // Cada 250 milisegundos redefinimos los caractéres
    instanteAnteriorCaracter = instanteActual;
    pasoCaracter = (pasoCaracter + 1) % 4;                      // Cada vez que pasa por aquí, pasoCaracter de incrementa en uno volviendo a cero despues de valer 3
    if (FLECHAS) {  // Si se ha definido FLECHAS como true, animará unas flechas ascendentes y descendentes
      lcd.createChar(CARACTER_CALENTAR, cararterSubir[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterBajar[pasoCaracter]);
    }
    else {          // Si se ha definido FLECHAS como false, animará "otra cosas"
      lcd.createChar(CARACTER_CALENTAR, cararterCalentar[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterEnfriar[pasoCaracter]);
    }
  }
  // Lectura de la temperatura cada cierto intervalo y control de si se sale del rango "válido" (de -99º a 200º)
  if ((instanteActual - instanteAnteriorTemperatura) >= INTERVALO_ENTRE_LECTURAS) {
    instanteAnteriorTemperatura = instanteActual;
    temperatura = thermocouple.readCelsius();
    temperaturaErronea = !((temperatura < 200) && (temperatura > -99));
  }
  // Control del commutador que selecciona la temperatura máxima final
  temperaturaCalentamientoFinal = (digitalRead(PIN_SELECTOR_TEMPERATURA) ? MAXIMO_CALENTANDO_FINAL_A : MAXIMO_CALENTANDO_FINAL_B);
  if (temperaturaCalentamientoFinal != temperaturaCalentamientoFinalAnterior) {
    temperaturaCalentamientoFinalAnterior = temperaturaCalentamientoFinal;
  }
  // Control de la máquina de estados "principal"
  switch (estado) {
    case ESTADO_INICIAL :
      // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
      instanteAnterior = instanteActual;    // Nos quedamos con el instante en que empieza a contar el tiempo
      estado = ESTADO_PRESENTACION;         // Pasamos a esperar a que termine el tiempo de la presentación
      estadoLCD = ESTADO_LCD_PRESENTACION;  // Cambia de estado la máquina de estados del LCD para que muestre la presentación
      break;
    case ESTADO_PRESENTACION :
      // Espera que transcurra el tiempo de presentación para luego pasar a calentar
      if ((instanteActual - instanteAnterior) >= 3000UL) {        // Si ha transcurrido el tiempo deseado
        estadoLCD = ESTADO_LCD_PREPARAR;                          // Le dice a la máquina de estados del LCD que muestre los datos "estáticos"
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador
        if (CANTIDAD_CICLOS == 0) {
          estado = ESTADO_CALENTANDO_FINAL;   // Si no hay ciclos intermedios, entonces pasa directamente al calentamiento final
        }
        else {                                // Si hay ciclos
          ciclo = 1;                          // Está en el primer ciclo
          estado = ESTADO_CALENTANDO_CICLO;   // Pasa a calentar un ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_CICLO :
      // Está calentando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= MAXIMO_CALENTANDO) {                   // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_CICLO;                        // Cambia al estado de enfriamiento del ciclo
      }
      break;
    case ESTADO_ENFRIANDO_CICLO :
      // Está enfriando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO) {                      // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        if (ciclo >= CANTIDAD_CICLOS) {                           // Si estaba en el último ciclo
          estado = ESTADO_CALENTANDO_FINAL;                       // Se pasa a el calentamiento final
        }
        else {                                                    // Si no estaba en el último ciclo
          ciclo++;                                                // Se incrementa el contador de ciclos (el famoso for)
          estado = ESTADO_CALENTANDO_CICLO;                       // Se vuleve a calentar del ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_FINAL :
      // Está en el calentantamiento del final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= temperaturaCalentamientoFinal) {       // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_FINAL;                        // Pasa al enfriamiento final
      }
      break;
    case ESTADO_ENFRIANDO_FINAL :
      // Está en el enfriado final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO_FINAL) {              // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Apaga el ventilador
        estado = ESTADO_FINAL;                                  // Cambia al estado de finalización del proceso
      }
      break;
    case ESTADO_ERROR_TEMPERATURA :
      // Se ha detectado un error en el sensor de temperatura
      // Nos aseguramos de que se apaga el calentador y el ventilador
      digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);    // Apaga el calentador
      digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);    // Apaga el ventilador
      estado = ESTADO_PAUSA_ERROR_TEMPERATURA;                  // Pasa a esperar a que vuelva a haber una lectura correcta de la temperatura
      break;
    case ESTADO_PAUSA_ERROR_TEMPERATURA :
      // Espera a que se quite el error del sensor de temperatura
      if (!temperaturaErronea) {          // Si la temperatura no es errónea (es correcta)
        estado = estadoGuardado;          // Recupera el estado guardado de la máquina de estado
        switch (estado) {
          case ESTADO_CALENTANDO_CICLO :
          case ESTADO_CALENTANDO_FINAL :
            digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador sólo para los estados ESTADO_CALENTANDO_CICLO y ESTADO_CALENTANDO_FINAL
          case ESTADO_ENFRIANDO_CICLO :
          case ESTADO_ENFRIANDO_FINAL :
            digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador para los cuatro estados
            break;
          default :
            // No hace enada para todos los demás estados
            break;
        }
      }
      break;
    case ESTADO_FINAL :
      // Ha finalizado todo el proceso
      // No hace nada
      break;
  }

  // Control de lo que se muestra en el LCD ayudado por una máquina de estados propia
  // .. y consultando el estado de la máquina de estado principal
  switch (estadoLCD) {
    case ESTADO_LCD_OCIOSO :
      // No ha de hacer nada con el LCD, más que dejarlo tal cual está
      // No hace nada
      break;
    case ESTADO_LCD_PRESENTACION :
      // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("D. Marmo Servicios")); // Mensaje a desplegar
      lcd.setCursor(0, 2);
      lcd.print(F("    Secadora Do 6 ")); // Mensaje 2 a desplegar
      estadoLCD = ESTADO_LCD_OCIOSO;      // Pasa a no hacer nada, hasta que la máquina de estados principal le diga que haga algo
      break;
    case ESTADO_LCD_PREPARAR :
      // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
      lcd.clear();
      if (CANTIDAD_CICLOS != 0) {
        // Si hay ciclos, se muestra las temperaturas de los ciclos
        lcd.setCursor(1, 2);
        lcd.print(MAXIMO_CALENTANDO);
        lcd.write(223);
        lcd.print('/');
        lcd.print(MINIMO_ENFRIANDO);
        lcd.write(223);
      }
      // Se muestran las temperaturas finales
      lcd.setCursor(1, 3);
      lcd.print(temperaturaCalentamientoFinal); // El valor de esta temperatura se ha calculado según el conmutador selector
      lcd.write(223);
      lcd.print('/');
      lcd.print(MINIMO_ENFRIANDO_FINAL);
      lcd.write(223);
      estadoLCD = ESTADO_LCD_TRABAJANDO;        // Pasa al estado de "trabajo", donde muestra el resto de valores
    // No ponemos break ya que queremos que realice las tareas del estado ESTADO_LCD_TRABAJANDO que ha de tratarse a continuación
    case ESTADO_LCD_TRABAJANDO :
      // Muestra y actualiza la información
      { // Creamos un bloque de código dentro de este case porque las siguientes variables sólo van a ser usadas en este case
        bool mostrarPasos = false;    // Tan sólo es para saber si ha de actualizar por qué paso va, ya que está dentro de un ciclo
        int temperaturaDestino = -1;  // Temperatura que se quiere alcanzar. Inicializada a -1 por si no se le asigna valor, que tenga algo que mostrar
        char subeBaja = ' ';          // Carárter animado que indica si se está calentando o enfriando
        char calientaCiclo = ' ';     // Carárter animado que se muestra junto a la temperatura máxima del ciclo
        char enfriaCiclo = ' ';       // Carárter animado que se muestra junto a la temperatura mínima del ciclo
        char calientaFinal = ' ';     // Carárter animado que se muestra junto a la temperatura máxima final
        char enfriaFinal = ' ';       // Carárter animado que se muestra junto a la temperatura mínima final
        lcd.setCursor(1, 0);          // Posiciona el cursor para poner qué es lo que está haciendo
        // Aprovechamos la máquina de estados «principal» para saber qué hemos de mostrar
        switch (estado) {
          case ESTADO_INICIAL :
          case ESTADO_PRESENTACION :
            // No hace nada. Es más, no debería de darse nunca esta condición
            break;
          case ESTADO_CALENTANDO_CICLO :
            lcd.print(F("CALENTANDO"));             // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;           // Animación de calentar
            calientaCiclo = CARACTER_CALENTAR;      // Animación de calentar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MAXIMO_CALENTANDO; // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ENFRIANDO_CICLO :
            lcd.print(F("ENFRIANDO "));             // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;            // Animación de enfriar
            enfriaCiclo = CARACTER_ENFRIAR;         // Animación de enfriar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MINIMO_ENFRIANDO;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_CALENTANDO_FINAL :
            lcd.print(F("CALENTANDO"));                         // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;                       // Animación de calentar
            calientaFinal = CARACTER_CALENTAR;                  // Animación de calentar
            temperaturaDestino = temperaturaCalentamientoFinal; // Temperatura que se ha de alcanzar, que depende el conmutador de selección
            break;
          case ESTADO_ENFRIANDO_FINAL :
            lcd.print(F("ENFRIANDO "));                   // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;                  // Animación de enfriar
            enfriaFinal = CARACTER_ENFRIAR;               // Animación de enfriar
            temperaturaDestino = MINIMO_ENFRIANDO_FINAL;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ERROR_TEMPERATURA :       // Tanto en el estado ESTADO_ERROR_TEMPERATURA...
          case ESTADO_PAUSA_ERROR_TEMPERATURA : // ... como en el estado ESTADO_PAUSA_ERROR_TEMPERATURA
            if ((instanteActual / 500) & 1) {   // Se muestra parpadeando el mensaje de aviso
              lcd.print(F("SENSOR MAL"));
            }
            else {
              lcd.print(F("          "));
            }
            // Se va a mostrar la temperatura destino dependiendo del estado en que estaba la máquina de estado principal cuando ha fallado la temperatura
            switch (estadoGuardado) {
              case ESTADO_CALENTANDO_CICLO :
                temperaturaDestino = MAXIMO_CALENTANDO;
                break;
              case ESTADO_CALENTANDO_FINAL :
                temperaturaDestino = temperaturaCalentamientoFinal;
                break;
              case ESTADO_ENFRIANDO_CICLO :
                temperaturaDestino = MINIMO_ENFRIANDO;
                break;
              case ESTADO_ENFRIANDO_FINAL :
                temperaturaDestino = MINIMO_ENFRIANDO_FINAL;
              default :
                break;
            }
            break;
          case ESTADO_FINAL :
            // La máquina de estados principal ha finalizado
            estadoLCD = ESTADO_LCD_MUESTRA_FINAL; // La máquina de estados del LCD pasa a mostrar el mensaje final
            break;
        }
        // Muestra por qué ciclo va
        if (mostrarPasos) {
          lcd.setCursor(3 - (ciclo / 10), 1);
          lcd.print(ciclo);
          lcd.print('/');
          lcd.print(CANTIDAD_CICLOS);
          lcd.print('x');
        }
        // Muestra la temperatura destino
        lcd.setCursor(14, 1);
        lcd.print(temperaturaDestino);
        lcd.write(223);
        lcd.write(subeBaja);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(0, 2);
        lcd.write(calientaCiclo);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 2);
        lcd.write(enfriaCiclo);
        // Muestra la temperatura máxima final (que puede variar)
        lcd.setCursor(0, 3);
        lcd.write(calientaFinal);
        lcd.print(temperaturaCalentamientoFinal);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 3);
        lcd.write(enfriaFinal);
      }
    case ESTADO_LCD_TEMPERATURA :
      // Muestra actualizado el valor de la temperatura
      lcd.setCursor(13, 0);
      if (temperaturaErronea) {
        lcd.print(F(" --.--")); // Es lo que muestra si la lectura de temperatura no es correcta
      }
      else {
        if ((temperatura < 100) && (temperatura > -10)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        if ((temperatura < 10) && (temperatura >= 0)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        lcd.print(temperatura);
      }
      lcd.write(223);
      break;
    case ESTADO_LCD_MUESTRA_FINAL :
      // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
      lcd.clear();
      lcd.setCursor(3, 2);
      lcd.print(F("FIN DE PROCESO"));
      estadoLCD = ESTADO_LCD_TEMPERATURA;
      break;
  }
  
  }


Saludos.

Nota: el problema empieza en el renglón 283.

La variable 'bucle' no la has declarado en níngún sitio y por ahí empieza a dar problemas.

La forma más básica de hacer menu con una máquina de estados es que cada pantalla sea un estado. En ese estado va a realizar alguna acción o nada. Lo logico es que cuando pulses botones en un estado hagas algo y que con otro botón salgas de ese estado/pantalla y vayas a otro. Un ejemplo en pseudocódigo:

switch ( estado ) {
  ...
  case LEYENDO_VARIABLE:
    lcd.print(variable);
    if ( botonsubir ) variable++;
    if ( botonbajar ) variable--;
    if ( botonmenu ) estado = otra_pantalla;
  }
  ...

Si estas leyendo una variable, la muestras en pantalla. Cuando apretas subir incrementas la variable, la decrementas si pulsas bajar y con el botón de menú sales a otro menu.

Tips o trucos:

  • El estado es una variable de tipo int. Generalemente por comodidad y legibiladad del código se usa enum para crear una enumeración con los estados, esto es simplemente "nombres" para ese número.
// Declaramos los estados
enum ESTADOS {
  ESTADO1, // <- Esto es 0.
  ESTADO2, // <- Esto es 1.
  ESTADO3, // <- Esto es 2.
  ESTADO4  // <- Esto es 3.
};
// la variable donde se guardará el estado:
ESTADOS estado;
  • Lo mas cómodo para trabajar con varios estados es usar un switch con un case para estado.
switch ( estado ) {
  case ESTADO1: break;
  case ESTADO2: break;
  case ESTADO3: break;
  case ESTADO4: break;
  default: break;

Aunque también se puede hacer con if-else lo más legible es usar un switch si tuvieras pocos estados quizás no merezca la pena usar un switch.

  • Uno de los problemas de máquinas de estados y menus es el problema de repintado de pantalla, si haces esto:
  case ESTADO1:
    lcd.clear();
    lcd.print("VARIABLE:");
    lcd.print(variable);
    break;

Posiblemente no llegues nunca a ver el menu porqué borras la pantalla continuamente y nunca verás nada. Por eso muchas veces hay que
jugar con setCursor sin borrar la pantalla, escribiendo caracteres en blanco algunas veces para borrar.

  • Puedes usar eventos al salir de un estado. Por ejemplo:
  case ESTADO1:
    if ( botonmenu ) {
      lcd.clear();
      estado=ESTADO2;
    }

En este ejemplo, si pulsas el boton de menu se ira a otra pantalla/estado, pero antes hará un clear de la pantalla. Con esta tecnica evitaras lo del problema anterior muchas veces.

En el código de @IgnoranteAbsoluto se ha aplicado bastantes de estos tips, solo tienes que ir leyendo y entendiendo que hace en cada pantalla/estado.

A grosos modos es como funciona la máquina de estados. La complejidad depende de los estados que haya y de los eventos o transacciones que se hagan en cada estado, no solo estás limitado a los botones, tambien puedes cambiar de estado si ocurre alguna
otra cosa.

Para menus complejos yo tengo una libreria que encapsula una máquina de estados que me facilita el trabajo ya que solo tengo que definir los estados, eventos y funciones con lo que va a hacer en cada caso.

Hola @victorjam, si el codigo de @IgnoranteAbsoluto es estate machine puro, ya tiene 2 enum uno para controlar la temperatura, ( enum estado_t { ) y otro para la LCD ( enum estado_pantalla_t { ), voy a iniciar otro enum empezare a ponerlo, como tercer enum estado_botones_t espero avanzar mas que con mi anterior intento.
Gracias

Hola @victorjam, si el codigo de @IgnoranteAbsoluto es estate machine puro, ya tiene 2 enum uno para controlar la temperatura y ventilador, y otro para la LCD, voy a crear otro enum empezare a ponerlo como tercer (enum estado_botones_t) haber hasta donde logro llegar, también pondré botones, después del switch case LCD, [ if ( botonmenu ); ]espero y funcione
Gracias, @victorjam.
saludos.

#include "max6675.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20 , 4);
// 0x27 0x3F , 0x20 , 0x38 ,  16 , 2 20 , 4

const byte CANTIDAD_CICLOS  = 2;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 39.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 36.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 40.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 42.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 29.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador
const unsigned long INTERVALO_ENTRE_LECTURAS = 2500UL;  // Milisegundos que han de transcurrir entre cada lectura de la temperatura
const bool FLECHAS = true;                              // Cambia las "animaciones" dependiedo de si se define true o false

// Definición de los pines usados
const byte THERMO_DO  = 6;  // Pines a los que está conectado el sensor de termperatura
const byte THERMO_CS  = 5;
const byte THERMO_CLK = 4;
const byte PIN_SELECTOR_TEMPERATURA = 11;   // Pin de selector de temperatura final (opción A o B)
const byte PIN_RELE_CALENTADOR      = 12;   // Pin al que está conectado el relé del calentador
const byte PIN_RELE_VENTILADOR      = 13;   // Pin al que está conectado el relé del ventilador

const byte VALOR_RELE_ENCENDIDO = LOW;                   // Valor que ha de tener el pin para que se active el relé
const byte VALOR_RELE_APAGADO   = !VALOR_RELE_ENCENDIDO; // Esta definición no hay que tocarla nunca, se ha de modificar la de VALOR_RELE_ENCENDIDO si fuera necesario

const char CARACTER_CALENTAR = 0; // Carácter que se usa para la animación que indica que se está calentando
const char CARACTER_ENFRIAR = 1;  // Carácter que se usa para la animación que indica que se está enfriando

// Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
const byte vccPin      = 3;
const byte gndPin      = 2;
const byte boton_menu  = 8;
const byte boton_sube  = 9;
const byte boton_baja  = 10;
MAX6675 thermocouple(THERMO_CLK, THERMO_CS, THERMO_DO);
float temperatura = 0;                          // Almacena el valor de la temperatura
bool temperaturaErronea = false;                // Se pone a true si se detecta una temperatura anómala
unsigned long instanteAnterior = 0;             // Para controlar las pausas en la máquina de estados
unsigned long instanteAnteriorTemperatura = 0;  // Para controlar el tiempo transcurrido desde la última lectura de temperatura
unsigned long instanteAnteriorCaracter = 0;     // Para controlar el tiempo de la animación de los caractéres
byte ciclo = 0;                                 // Lleva la cuenta del ciclo en que se encuentra
byte pasoCaracter = 0;                          // Para controlar en que paso está de la animación de los caractéres (todos tienen cuato pasos)
int temperaturaCalentamientoFinal = 0;          // Almacena el valor de la temperatura del calentamiento final que vale según la posición del delector de temperatura
int temperaturaCalentamientoFinalAnterior = 0;  // Tan solo es para ver si ha cambiado el valor de temperaturaCalentamientoFinal. Si es así, actualiza su visualización en el LCD

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados "principal"
enum estado_t {
  ESTADO_INICIAL,                 // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
  ESTADO_PRESENTACION,            // Espera que transcurra el tiempo de presentación para luego pasar a calentar
  ESTADO_CALENTANDO_CICLO,        // Está calentando durante uno de los ciclos intermedios
  ESTADO_ENFRIANDO_CICLO,         // Está enfriando durante uno de los ciclos intermedios
  ESTADO_CALENTANDO_FINAL,        // Está en el calentantamiento del final
  ESTADO_ENFRIANDO_FINAL,         // Está en el enfriado final
  ESTADO_ERROR_TEMPERATURA,       // Se ha detectado un error en el sensor de temperatura
  ESTADO_PAUSA_ERROR_TEMPERATURA, // Espera a que se quite el error del sensor de temperatura
  ESTADO_FINAL                    // Ha finalizado todo el proceso
};
estado_t estado = ESTADO_INICIAL;         // Variable en la que se guarda el estado de la máquina de estado principal
estado_t estadoGuardado = ESTADO_INICIAL; // Guarda el estado en que estaba cuando se ha producido el error de temperatura, para volver a ese estado cuando se subsane el error

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados que controla la visualizacón del LCD
enum estado_pantalla_t {
  ESTADO_LCD_OCIOSO,              // No ha de hacer nada con el LCD, más que dejarlo tal cual está
  ESTADO_LCD_PRESENTACION,        // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
  ESTADO_LCD_PREPARAR,            // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
  ESTADO_LCD_TRABAJANDO,          // Muestra y actualiza la información
  ESTADO_LCD_TEMPERATURA,         // Muestra actualizado el valor de la temperatura
  ESTADO_LCD_MUESTRA_FINAL        // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
} estadoLCD = ESTADO_LCD_OCIOSO;  // Variable en la que se guarda el estado de la máquina de estados del LCD

enum estado_botones_t {
  ESTADO_BOTON_INICIA_MENU,                 // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
  ESTADO_BOTON_ENTRAR_MENU1,            // Espera que transcurra el tiempo de presentación para luego pasar a calentar
  ESTADO_BOTON_CALENTANDO_CICLO,        // Está calentando durante uno de los ciclos intermedios
  ESTADO_BOTON_ENFRIANDO_CICLO,         // Está enfriando durante uno de los ciclos intermedios
  ESTADO_BOTON_CALENTANDO_FINAL,        // Está en el calentantamiento del final
  ESTADO_BOTON_ENFRIANDO_FINAL,         // Está en el enfriado final
  // ESTADO_ERROR_TEMPERATURA_BOTON,       // Se ha detectado un error en el sensor de temperatura
  // ESTADO_PAUSA_ERROR_TEMPERATURA, // Espera a que se quite el error del sensor de temperatura
  // ESTADO_FINAL                    // Ha finalizado todo el proceso
};

// Definiciones de los caractéres para la animación
byte cararterSubir[4][8] = {  // Las flechas que suben
  {
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001
  }
  , {
    B00100,
    B01010,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000
  }
  , {
    B00000,
    B00000,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000,
    B00000
  }
  , {
    B00000,
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01010,
    B10001
  }
};

byte cararterBajar[4][8] = {  // Las flechas que bajan
  {
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100
  }
  , {
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B01010,
    B00100
  }
  , {
    B00000,
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B00000,
    B00000
  }
  , {
    B10001,
    B01010,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100,
    B00000
  }
};

byte cararterCalentar[4][8] = { // Calentando
  {
    B00001,
    B01001,
    B10010,
    B10010,
    B01001,
    B01001,
    B10100,
    B01110
  }
  , {
    B01000,
    B10010,
    B10010,
    B01001,
    B01001,
    B10010,
    B00110,
    B01110
  }
  , {
    B00010,
    B10010,
    B01001,
    B01001,
    B10010,
    B10010,
    B01100,
    B01110
  }
  , {
    B10000,
    B01001,
    B01001,
    B10010,
    B10010,
    B01001,
    B00101,
    B01110
  }
};

byte cararterEnfriar[4][8] = {  // Enfriando
  {
    B00000,
    B00110,
    B11001,
    B00000,
    B10011,
    B01100,
    B00100,
    B01110
  }
  , {
    B00000,
    B10011,
    B01100,
    B00000,
    B11001,
    B00110,
    B00100,
    B01110
  }
  , {
    B00000,
    B11001,
    B00110,
    B00000,
    B01100,
    B10011,
    B00100,
    B01110
  }
  , {
    B00000,
    B01100,
    B10011,
    B00000,
    B00110,
    B11001,
    B00100,
    B01110
  }
};

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(PIN_RELE_CALENTADOR, OUTPUT);
  digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Inicialmente el calentador está apagado
  pinMode(PIN_RELE_VENTILADOR, OUTPUT);
  digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Inicialmente el ventilador está apagado
  pinMode(PIN_SELECTOR_TEMPERATURA, INPUT);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(vccPin, OUTPUT);
  digitalWrite(vccPin, HIGH);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(gndPin, OUTPUT);
  digitalWrite(gndPin, LOW);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(boton_menu, INPUT_PULLUP);
  digitalWrite(boton_menu, LOW);

  pinMode(boton_sube, INPUT_PULLUP);
  digitalWrite(boton_sube, LOW);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(boton_baja, INPUT_PULLUP );
  digitalWrite(boton_baja, LOW);
}

void loop() {
  unsigned long instanteActual = millis();                      // Tomamos el instante actual, para controlar los tiempos
  // Animación de los caracteres animados
  if ((instanteActual - instanteAnteriorCaracter) >= 250UL) {   // Cada 250 milisegundos redefinimos los caractéres
    instanteAnteriorCaracter = instanteActual;
    pasoCaracter = (pasoCaracter + 1) % 4;                      // Cada vez que pasa por aquí, pasoCaracter de incrementa en uno volviendo a cero despues de valer 3
    if (FLECHAS) {  // Si se ha definido FLECHAS como true, animará unas flechas ascendentes y descendentes
      lcd.createChar(CARACTER_CALENTAR, cararterSubir[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterBajar[pasoCaracter]);
    }
    else {          // Si se ha definido FLECHAS como false, animará "otra cosas"
      lcd.createChar(CARACTER_CALENTAR, cararterCalentar[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterEnfriar[pasoCaracter]);
    }
  }
  // Lectura de la temperatura cada cierto intervalo y control de si se sale del rango "válido" (de -99º a 200º)
  if ((instanteActual - instanteAnteriorTemperatura) >= INTERVALO_ENTRE_LECTURAS) {
    instanteAnteriorTemperatura = instanteActual;
    temperatura = thermocouple.readCelsius();
    temperaturaErronea = !((temperatura < 90) && (temperatura > -99));
   // if(temperaturaErronea > 90);
    //digitalWrite(PIN_RELE_CALENTADOR, HIGH); 
  }
  // Control del commutador que selecciona la temperatura máxima final
  temperaturaCalentamientoFinal = (digitalRead(PIN_SELECTOR_TEMPERATURA) ? MAXIMO_CALENTANDO_FINAL_A : MAXIMO_CALENTANDO_FINAL_B);
  if (temperaturaCalentamientoFinal != temperaturaCalentamientoFinalAnterior) {
    temperaturaCalentamientoFinalAnterior = temperaturaCalentamientoFinal;
  }
  // Control de la máquina de estados "principal"
  switch (estado) {
    case ESTADO_INICIAL :
      // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
      instanteAnterior = instanteActual;    // Nos quedamos con el instante en que empieza a contar el tiempo
      estado = ESTADO_PRESENTACION;         // Pasamos a esperar a que termine el tiempo de la presentación
      estadoLCD = ESTADO_LCD_PRESENTACION;  // Cambia de estado la máquina de estados del LCD para que muestre la presentación
      break;
    case ESTADO_PRESENTACION :
      // Espera que transcurra el tiempo de presentación para luego pasar a calentar
      if ((instanteActual - instanteAnterior) >= 3000UL) {        // Si ha transcurrido el tiempo deseado
        estadoLCD = ESTADO_LCD_PREPARAR;                          // Le dice a la máquina de estados del LCD que muestre los datos "estáticos"
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador
        if (CANTIDAD_CICLOS == 0) {
          estado = ESTADO_CALENTANDO_FINAL;   // Si no hay ciclos intermedios, entonces pasa directamente al calentamiento final
        }
        else {                                // Si hay ciclos
          ciclo = 1;                          // Está en el primer ciclo
          estado = ESTADO_CALENTANDO_CICLO;   // Pasa a calentar un ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_CICLO :
      // Está calentando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= MAXIMO_CALENTANDO) {                   // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_CICLO;                        // Cambia al estado de enfriamiento del ciclo
      }
      break;
    case ESTADO_ENFRIANDO_CICLO :
      // Está enfriando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO) {                      // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        if (ciclo >= CANTIDAD_CICLOS) {                           // Si estaba en el último ciclo
          estado = ESTADO_CALENTANDO_FINAL;                       // Se pasa a el calentamiento final
        }
        else {                                                    // Si no estaba en el último ciclo
          ciclo++;                                                // Se incrementa el contador de ciclos (el famoso for)
          estado = ESTADO_CALENTANDO_CICLO;                       // Se vuleve a calentar del ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_FINAL :
      // Está en el calentantamiento del final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= temperaturaCalentamientoFinal) {       // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_FINAL;                        // Pasa al enfriamiento final
      }
      break;
    case ESTADO_ENFRIANDO_FINAL :
      // Está en el enfriado final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO_FINAL) {              // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Apaga el ventilador
        estado = ESTADO_FINAL;                                  // Cambia al estado de finalización del proceso
      }
      break;
    case ESTADO_ERROR_TEMPERATURA :
      // Se ha detectado un error en el sensor de temperatura
      // Nos aseguramos de que se apaga el calentador y el ventilador
      digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);    // Apaga el calentador
      digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);    // Apaga el ventilador
      estado = ESTADO_PAUSA_ERROR_TEMPERATURA;                  // Pasa a esperar a que vuelva a haber una lectura correcta de la temperatura
      break;
    case ESTADO_PAUSA_ERROR_TEMPERATURA :
      // Espera a que se quite el error del sensor de temperatura
      if (!temperaturaErronea) {          // Si la temperatura no es errónea (es correcta)
        estado = estadoGuardado;          // Recupera el estado guardado de la máquina de estado
        switch (estado) {
          case ESTADO_CALENTANDO_CICLO :
          case ESTADO_CALENTANDO_FINAL :
            digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador sólo para los estados ESTADO_CALENTANDO_CICLO y ESTADO_CALENTANDO_FINAL
          case ESTADO_ENFRIANDO_CICLO :
          case ESTADO_ENFRIANDO_FINAL :
            digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador para los cuatro estados
            break;
          default :
            // No hace enada para todos los demás estados
            break;
        }
      }
      break;
    case ESTADO_FINAL :
      // Ha finalizado todo el proceso
      // No hace nada
      break;
  }

  // Control de lo que se muestra en el LCD ayudado por una máquina de estados propia
  // .. y consultando el estado de la máquina de estado principal
  switch (estadoLCD) {
    case ESTADO_LCD_OCIOSO :
      // No ha de hacer nada con el LCD, más que dejarlo tal cual está
      // No hace nada
      break;
    case ESTADO_LCD_PRESENTACION :
      // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("DESHIDRATADORA")); // Mensaje a desplegar
      lcd.setCursor(0, 1);
      lcd.print(F(" PERA 5 M.M. ")); // Mensaje 2 a desplegar
      estadoLCD = ESTADO_LCD_OCIOSO;      // Pasa a no hacer nada, hasta que la máquina de estados principal le diga que haga algo
      break;
    case ESTADO_LCD_PREPARAR :
      // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
      lcd.clear();
      if (CANTIDAD_CICLOS != 0) {
        // Si hay ciclos, se muestra las temperaturas de los ciclos
        lcd.setCursor(1, 2);
        lcd.print(MAXIMO_CALENTANDO);
        lcd.write(223);
        lcd.print('/');
        lcd.print(MINIMO_ENFRIANDO);
        lcd.write(223);
      }
      // Se muestran las temperaturas finales
      lcd.setCursor(1, 3);
      lcd.print(temperaturaCalentamientoFinal); // El valor de esta temperatura se ha calculado según el conmutador selector
      lcd.write(223);
      lcd.print('/');
      lcd.print(MINIMO_ENFRIANDO_FINAL);
      lcd.write(223);
      estadoLCD = ESTADO_LCD_TRABAJANDO;        // Pasa al estado de "trabajo", donde muestra el resto de valores
    // No ponemos break ya que queremos que realice las tareas del estado ESTADO_LCD_TRABAJANDO que ha de tratarse a continuación
    case ESTADO_LCD_TRABAJANDO :
      // Muestra y actualiza la información
      { // Creamos un bloque de código dentro de este case porque las siguientes variables sólo van a ser usadas en este case
        bool mostrarPasos = false;    // Tan sólo es para saber si ha de actualizar por qué paso va, ya que está dentro de un ciclo
        int temperaturaDestino = -1;  // Temperatura que se quiere alcanzar. Inicializada a -1 por si no se le asigna valor, que tenga algo que mostrar
        char subeBaja = ' ';          // Carárter animado que indica si se está calentando o enfriando
        char calientaCiclo = ' ';     // Carárter animado que se muestra junto a la temperatura máxima del ciclo
        char enfriaCiclo = ' ';       // Carárter animado que se muestra junto a la temperatura mínima del ciclo
        char calientaFinal = ' ';     // Carárter animado que se muestra junto a la temperatura máxima final
        char enfriaFinal = ' ';       // Carárter animado que se muestra junto a la temperatura mínima final
        lcd.setCursor(1, 0);          // Posiciona el cursor para poner qué es lo que está haciendo
        // Aprovechamos la máquina de estados «principal» para saber qué hemos de mostrar
        switch (estado) {
          case ESTADO_INICIAL :
          case ESTADO_PRESENTACION :
            // No hace nada. Es más, no debería de darse nunca esta condición
            break;
          case ESTADO_CALENTANDO_CICLO :
            lcd.print(F("CALENTANDO"));             // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;           // Animación de calentar
            calientaCiclo = CARACTER_CALENTAR;      // Animación de calentar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MAXIMO_CALENTANDO; // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ENFRIANDO_CICLO :
            lcd.print(F("ENFRIANDO "));             // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;            // Animación de enfriar
            enfriaCiclo = CARACTER_ENFRIAR;         // Animación de enfriar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MINIMO_ENFRIANDO;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_CALENTANDO_FINAL :
            lcd.print(F("CALENTANDO"));                         // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;                       // Animación de calentar
            calientaFinal = CARACTER_CALENTAR;                  // Animación de calentar
            temperaturaDestino = temperaturaCalentamientoFinal; // Temperatura que se ha de alcanzar, que depende el conmutador de selección
            break;
          case ESTADO_ENFRIANDO_FINAL :
            lcd.print(F("ENFRIANDO "));                   // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;                  // Animación de enfriar
            enfriaFinal = CARACTER_ENFRIAR;               // Animación de enfriar
            temperaturaDestino = MINIMO_ENFRIANDO_FINAL;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ERROR_TEMPERATURA :       // Tanto en el estado ESTADO_ERROR_TEMPERATURA...
          case ESTADO_PAUSA_ERROR_TEMPERATURA : // ... como en el estado ESTADO_PAUSA_ERROR_TEMPERATURA
            if ((instanteActual / 500) & 1) {   // Se muestra parpadeando el mensaje de aviso
              lcd.print(F("SENSOR MAL"));
            }
            else {
              lcd.print(F("          "));
              
            }
            // Se va a mostrar la temperatura destino dependiendo del estado en que estaba la máquina de estado principal cuando ha fallado la temperatura
            switch (estadoGuardado) {
              case ESTADO_CALENTANDO_CICLO :
                temperaturaDestino = MAXIMO_CALENTANDO;
                break;
              case ESTADO_CALENTANDO_FINAL :
                temperaturaDestino = temperaturaCalentamientoFinal;
                break;
              case ESTADO_ENFRIANDO_CICLO :
                temperaturaDestino = MINIMO_ENFRIANDO;
                break;
              case ESTADO_ENFRIANDO_FINAL :
                temperaturaDestino = MINIMO_ENFRIANDO_FINAL;
              default :
                break;
            }
            break;
          case ESTADO_FINAL :
            // La máquina de estados principal ha finalizado
            estadoLCD = ESTADO_LCD_MUESTRA_FINAL; // La máquina de estados del LCD pasa a mostrar el mensaje final
            break;
        }
        // Muestra por qué ciclo va
        if (mostrarPasos) {
          lcd.setCursor(3 - (ciclo / 10), 1);
          lcd.print(ciclo);
          Serial.println(ciclo);
          lcd.print('/');
          lcd.print(CANTIDAD_CICLOS);
          lcd.print('x');
        }
        // Muestra la temperatura destino
        lcd.setCursor(14, 1);
        lcd.print(temperaturaDestino);
        lcd.write(223);
        lcd.write(subeBaja);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(0, 2);
        lcd.write(calientaCiclo);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 2);
        lcd.write(enfriaCiclo);
        // Muestra la temperatura máxima final (que puede variar)
        lcd.setCursor(0, 3);
        lcd.write(calientaFinal);
        lcd.print(temperaturaCalentamientoFinal);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 3);
        lcd.write(enfriaFinal);
      }
    case ESTADO_LCD_TEMPERATURA :
      // Muestra actualizado el valor de la temperatura
      lcd.setCursor(13, 0);
      if (temperaturaErronea) {
        lcd.print(F(" --.--")); // Es lo que muestra si la lectura de temperatura no es correcta
      }
      else {
        if ((temperatura < 100) && (temperatura > -10)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        if ((temperatura < 10) && (temperatura >= 0)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        lcd.print(temperatura);
      }
      lcd.write(223);
      break;
    case ESTADO_LCD_MUESTRA_FINAL :
      // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
      lcd.clear();
      lcd.setCursor(3, 2);
      lcd.print(F("FIN DE PROCESO"));
      estadoLCD = ESTADO_LCD_TEMPERATURA;
      break;
  }
  switch (estado) {

    case ESTADO_BOTON_INICIA_MENU :
      if (boton_menu, LOW);
      lcd.setCursor(1, 3);
      lcd.print("menu");
      lcd.print(boton_menu);
      break;

    case ESTADO_BOTON_ENTRAR_MENU1 :
      if (boton_menu, LOW);
      lcd.setCursor(12, 3);
      char *menu1[6] = {"ciclo ", "ciclo_sube ", "ciclo_baja", "final_sube", "final_baja", " fin "};
      lcd.println("menu1");
      break;

    case ESTADO_BOTON_CALENTANDO_CICLO :
      if (digitalRead, boton_sube, LOW);
       // if(boton_sube) CANTIDAD_CICLO ++;
      break;
  }
}




/*

  switch ( estado ) {
  ...
  case LEYENDO_VARIABLE:
    lcd.print(variable);
    if ( botonsubir ) variable++;
    if ( botonbajar ) variable--;
    if ( botonmenu ) estado = otra_pantalla;
  }

*/

En el renglón 607 me quede sin saber que hacer, no e podido acceder al menu. En la LCD aparece menu1 solo en la presentación junto a ( DESHIDRATADORA, PERA 5 M.M.), al iniciar el void loop los botones no hacen nada, pero bueno ya le veo algo de forma. siguiendo las indicaciones de @victorjam En el renglón 607 no doy cual sea el caracter (variable) correcto para que no me de error, y poder continuar.
Saludos.

if (digitalRead, boton_sube, LOW);
// if(boton_sube) CANTIDAD_CICLO ++;

Esta mal escrito...

if ( digitalRead(boton_sube)==LOW ) {
  CANTIDAD_CICLO++;
}

Aunque mi recomendación es que no leas los botones así. vas a tener muchos problemas. Te recomiendo que leas el tutorial que hice y entenderas por qué Como NO leer un botón y como SI debemos hacerlo.

Arduino:1.8.19 (Windows 10), Tarjeta:"Arduino Uno"

:\Users\silvi\Desktop\abosuto_botones_enum_3_if_603_error\abosuto_botones_enum_3_if_603_error.ino: In function 'void loop()':

abosuto_botones_enum_3_if_603_error:625:25: error: increment of read-only variable 'CANTIDAD_CICLOS'

         CANTIDAD_CICLOS ++;

                         ^~

exit status 1

increment of read-only variable 'CANTIDAD_CICLOS'



Este informe podría contener más información con
"Mostrar salida detallada durante la compilación"
opción habilitada en Archivo -> Preferencias.


Hola @victorjam, me sigue dando error el renglón 620. Ya le di una leída a tu tutorial " Como NO leer un botón y como SI debemos hacerlo. - #3 by victorjam " de botones cargue el post#4 y la botonera de membrana(post #5) funciona muy bien claro un solo botón como se hace para leer 4 botones sin repetir 4 veces el código de forma mas simple.
Saludos.
Nota: no me deja ensamblar tu código de botones me da error el renglón 275

Hola @victorjam, ya pude ensamblar tu codigo de botones al codigo deshidratadora pero no funcionan los botones lo hice creando un nuevo void botonera() {

#include "max6675.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20 , 4);
// 0x27 0x3F , 0x20 , 0x38 ,  16 , 2 20 , 4

const byte CANTIDAD_CICLOS  = 2;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 39.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 36.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 40.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 42.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 29.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador
const unsigned long INTERVALO_ENTRE_LECTURAS = 2500UL;  // Milisegundos que han de transcurrir entre cada lectura de la temperatura
const bool FLECHAS = true;                              // Cambia las "animaciones" dependiedo de si se define true o false

// Definición de los pines usados
const byte THERMO_DO  = 6;  // Pines a los que está conectado el sensor de termperatura
const byte THERMO_CS  = 5;
const byte THERMO_CLK = 4;
const byte PIN_SELECTOR_TEMPERATURA = 11;   // Pin de selector de temperatura final (opción A o B)
const byte PIN_RELE_CALENTADOR      = 12;   // Pin al que está conectado el relé del calentador
const byte PIN_RELE_VENTILADOR      = 13;   // Pin al que está conectado el relé del ventilador
const byte vccPin      = 3;
const byte gndPin      = 2;
const byte boton_menu  = 8;
const byte boton_sube  = 9;
const byte boton_baja  = 10;
const byte VALOR_RELE_ENCENDIDO = LOW;                   // Valor que ha de tener el pin para que se active el relé
const byte VALOR_RELE_APAGADO   = !VALOR_RELE_ENCENDIDO; // Esta definición no hay que tocarla nunca, se ha de modificar la de VALOR_RELE_ENCENDIDO si fuera necesario

const char CARACTER_CALENTAR = 0; // Carácter que se usa para la animación que indica que se está calentando
const char CARACTER_ENFRIAR = 1;  // Carácter que se usa para la animación que indica que se está enfriando

// Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello

MAX6675 thermocouple(THERMO_CLK, THERMO_CS, THERMO_DO);
float temperatura = 0;                          // Almacena el valor de la temperatura
bool temperaturaErronea = false;                // Se pone a true si se detecta una temperatura anómala
unsigned long instanteAnterior = 0;             // Para controlar las pausas en la máquina de estados
unsigned long instanteAnteriorTemperatura = 0;  // Para controlar el tiempo transcurrido desde la última lectura de temperatura
unsigned long instanteAnteriorCaracter = 0;     // Para controlar el tiempo de la animación de los caractéres
byte ciclo = 0;                                 // Lleva la cuenta del ciclo en que se encuentra
byte pasoCaracter = 0;                          // Para controlar en que paso está de la animación de los caractéres (todos tienen cuato pasos)
int temperaturaCalentamientoFinal = 0;          // Almacena el valor de la temperatura del calentamiento final que vale según la posición del delector de temperatura
int temperaturaCalentamientoFinalAnterior = 0;  // Tan solo es para ver si ha cambiado el valor de temperaturaCalentamientoFinal. Si es así, actualiza su visualización en el LCD

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados "principal"
enum estado_t {
  ESTADO_INICIAL,                 // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
  ESTADO_PRESENTACION,            // Espera que transcurra el tiempo de presentación para luego pasar a calentar
  ESTADO_CALENTANDO_CICLOS,        // Está calentando durante uno de los ciclos intermedios
  ESTADO_ENFRIANDO_CICLOS,         // Está enfriando durante uno de los ciclos intermedios
  ESTADO_CALENTANDO_FINAL,        // Está en el calentantamiento del final
  ESTADO_ENFRIANDO_FINAL,         // Está en el enfriado final
  ESTADO_ERROR_TEMPERATURA,       // Se ha detectado un error en el sensor de temperatura
  ESTADO_PAUSA_ERROR_TEMPERATURA, // Espera a que se quite el error del sensor de temperatura
  ESTADO_FINAL                    // Ha finalizado todo el proceso
};
estado_t estado = ESTADO_INICIAL;         // Variable en la que se guarda el estado de la máquina de estado principal
estado_t estadoGuardado = ESTADO_INICIAL; // Guarda el estado en que estaba cuando se ha producido el error de temperatura, para volver a ese estado cuando se subsane el error

// Definición del enumerado y de la variable que guarda en qué estado se encuentra la máquina de estados que controla la visualizacón del LCD
enum estado_pantalla_t {
  ESTADO_LCD_OCIOSO,              // No ha de hacer nada con el LCD, más que dejarlo tal cual está
  ESTADO_LCD_PRESENTACION,        // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
  ESTADO_LCD_PREPARAR,            // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
  ESTADO_LCD_TRABAJANDO,          // Muestra y actualiza la información
  ESTADO_LCD_TEMPERATURA,         // Muestra actualizado el valor de la temperatura
  ESTADO_LCD_MUESTRA_FINAL        // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
} estadoLCD = ESTADO_LCD_OCIOSO;  // Variable en la que se guarda el estado de la máquina de estados del LCD

enum estado_botones_t {
  ESTADO_BOTON_INICIA_MENU,                 // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
  ESTADO_BOTON_ENTRAR_MENU1,            // Espera que transcurra el tiempo de presentación para luego pasar a calentar
  ESTADO_BOTON_CALENTANDO_CICLO,        // Está calentando durante uno de los ciclos intermedios
  ESTADO_BOTON_ENFRIANDO_CICLO,         // Está enfriando durante uno de los ciclos intermedios
  ESTADO_BOTON_CALENTANDO_FINAL,        // Está en el calentantamiento del final
  ESTADO_BOTON_ENFRIANDO_FINAL,         // Está en el enfriado final
  // ESTADO_ERROR_TEMPERATURA_BOTON,       // Se ha detectado un error en el sensor de temperatura
  // ESTADO_PAUSA_ERROR_TEMPERATURA, // Espera a que se quite el error del sensor de temperatura
  // ESTADO_FINAL                    // Ha finalizado todo el proceso
};
#define APRETADO    0
#define SUELTO      1
#define APRETANDOLO 2
#define SOLTANDOLO  3

class BotonSimple {
  private:
    unsigned char pin;
    unsigned char anterior, valor;
    unsigned char estado;

    unsigned long temporizador;
    unsigned long tiempoRebote;
  public:
    BotonSimple(unsigned char _pin, unsigned long _tiempoRebote);
    void actualizar();
    int  leer();
};

BotonSimple::BotonSimple(unsigned char _pin, unsigned long _tiempoRebote=50) {
  pin = _pin;
  tiempoRebote = _tiempoRebote;
  pinMode(pin, INPUT_PULLUP);
  valor=HIGH; anterior=HIGH; estado=SUELTO;
}

void BotonSimple::actualizar() {
  // NOTA: En el ejemplo original en vez de "pin" leia directamente el pin "2", con lo que el
  // codigo no funciona correctamente con el pin que le hayamos asignado.
  if ( valor==digitalRead(pin) ){
    temporizador=0;
  }
  else
  if ( temporizador==0 ) {
    temporizador = millis();
  }
  else
  if ( millis()-temporizador >= tiempoRebote ) {
    valor = !valor;
  }
  if ( anterior==LOW  && valor==LOW  ) estado = APRETADO;
  if ( anterior==LOW  && valor==HIGH ) estado = SOLTANDOLO;
  if ( anterior==HIGH && valor==LOW  ) estado = APRETANDOLO;
  if ( anterior==HIGH && valor==HIGH ) estado = SUELTO;
  anterior = valor;
}

int BotonSimple::leer() { 
  return estado;
}
 

BotonSimple boton(8);

// Definiciones de los caractéres para la animación
byte cararterSubir[4][8] = {  // Las flechas que suben
  {
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001
  }
  , {
    B00100,
    B01010,
    B10001,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000
  }
  , {
    B00000,
    B00000,
    B00100,
    B01110,
    B11011,
    B10001,
    B00000,
    B00000
  }
  , {
    B00000,
    B00100,
    B01110,
    B11011,
    B10001,
    B00100,
    B01010,
    B10001
  }
};

byte cararterBajar[4][8] = {  // Las flechas que bajan
  {
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100
  }
  , {
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B10001,
    B01010,
    B00100
  }
  , {
    B00000,
    B00000,
    B10001,
    B11011,
    B01110,
    B00100,
    B00000,
    B00000
  }
  , {
    B10001,
    B01010,
    B00100,
    B10001,
    B11011,
    B01110,
    B00100,
    B00000
  }
};

byte cararterCalentar[4][8] = { // Calentando
  {
    B00001,
    B01001,
    B10010,
    B10010,
    B01001,
    B01001,
    B10100,
    B01110
  }
  , {
    B01000,
    B10010,
    B10010,
    B01001,
    B01001,
    B10010,
    B00110,
    B01110
  }
  , {
    B00010,
    B10010,
    B01001,
    B01001,
    B10010,
    B10010,
    B01100,
    B01110
  }
  , {
    B10000,
    B01001,
    B01001,
    B10010,
    B10010,
    B01001,
    B00101,
    B01110
  }
};

byte cararterEnfriar[4][8] = {  // Enfriando
  {
    B00000,
    B00110,
    B11001,
    B00000,
    B10011,
    B01100,
    B00100,
    B01110
  }
  , {
    B00000,
    B10011,
    B01100,
    B00000,
    B11001,
    B00110,
    B00100,
    B01110
  }
  , {
    B00000,
    B11001,
    B00110,
    B00000,
    B01100,
    B10011,
    B00100,
    B01110
  }
  , {
    B00000,
    B01100,
    B10011,
    B00000,
    B00110,
    B11001,
    B00100,
    B01110
  }
};
#define APRETADO    0
#define SUELTO      1
#define APRETANDOLO 2
#define SOLTANDOLO  3

class BotonSimple_menu1 {
  private:
    unsigned char pin;
    unsigned char anterior, valor;
    unsigned char estado;

    unsigned long temporizador;
    unsigned long tiempoRebote;
  public:
    BotonSimple_menu1(unsigned char _pin, unsigned long _tiempoRebote);
    void actualizar();
    int  leer();
};

BotonSimple_menu1::BotonSimple_menu1(unsigned char _pin, unsigned long _tiempoRebote = 50) {
  pin = _pin;
  tiempoRebote = _tiempoRebote;
  pinMode(pin, INPUT_PULLUP);
  valor = HIGH; anterior = HIGH; estado = SUELTO;
}


void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  pinMode(PIN_RELE_CALENTADOR, OUTPUT);
  digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Inicialmente el calentador está apagado
  pinMode(PIN_RELE_VENTILADOR, OUTPUT);
  digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Inicialmente el ventilador está apagado
  pinMode(PIN_SELECTOR_TEMPERATURA, INPUT);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(vccPin, OUTPUT);
  digitalWrite(vccPin, HIGH);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(gndPin, OUTPUT);
  digitalWrite(gndPin, LOW);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(boton_menu, INPUT_PULLUP);
  digitalWrite(boton_menu, HIGH);

  pinMode(boton_sube, INPUT_PULLUP);
  digitalWrite(boton_sube, HIGH);

  // Lo siguiente lo he respetado del código original porque no tengo claro qué es lo que se está haciendo con ello
  pinMode(boton_baja, INPUT_PULLUP );
  digitalWrite(boton_baja, HIGH);
}

void loop() {
  unsigned long instanteActual = millis();                      // Tomamos el instante actual, para controlar los tiempos
  // Animación de los caracteres animados
  if ((instanteActual - instanteAnteriorCaracter) >= 250UL) {   // Cada 250 milisegundos redefinimos los caractéres
    instanteAnteriorCaracter = instanteActual;
    pasoCaracter = (pasoCaracter + 1) % 4;                      // Cada vez que pasa por aquí, pasoCaracter de incrementa en uno volviendo a cero despues de valer 3
    if (FLECHAS) {  // Si se ha definido FLECHAS como true, animará unas flechas ascendentes y descendentes
      lcd.createChar(CARACTER_CALENTAR, cararterSubir[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterBajar[pasoCaracter]);
    }
    else {          // Si se ha definido FLECHAS como false, animará "otra cosas"
      lcd.createChar(CARACTER_CALENTAR, cararterCalentar[pasoCaracter]);
      lcd.createChar(CARACTER_ENFRIAR, cararterEnfriar[pasoCaracter]);
    }
  }
  // Lectura de la temperatura cada cierto intervalo y control de si se sale del rango "válido" (de -99º a 200º)
  if ((instanteActual - instanteAnteriorTemperatura) >= INTERVALO_ENTRE_LECTURAS) {
    instanteAnteriorTemperatura = instanteActual;
    temperatura = thermocouple.readCelsius();
    temperaturaErronea = !((temperatura < 50) && (temperatura > -99));
    // if(temperaturaErronea > 90);
    //digitalWrite(PIN_RELE_CALENTADOR, HIGH);
  }
  // Control del commutador que selecciona la temperatura máxima final
  temperaturaCalentamientoFinal = (digitalRead(PIN_SELECTOR_TEMPERATURA) ? MAXIMO_CALENTANDO_FINAL_A : MAXIMO_CALENTANDO_FINAL_B);
  if (temperaturaCalentamientoFinal != temperaturaCalentamientoFinalAnterior) {
    temperaturaCalentamientoFinalAnterior = temperaturaCalentamientoFinal;
  }
  // Control de la máquina de estados "principal"
  switch (estado) {
    case ESTADO_INICIAL :
      // Cuando arranca el Arduino. Guarda el instante actual para saber cuándo ha transcurrido el tiempo de presentación y pasa al estado ESTADO_PRESENTACION
      instanteAnterior = instanteActual;    // Nos quedamos con el instante en que empieza a contar el tiempo
      estado = ESTADO_PRESENTACION;         // Pasamos a esperar a que termine el tiempo de la presentación
      estadoLCD = ESTADO_LCD_PRESENTACION;  // Cambia de estado la máquina de estados del LCD para que muestre la presentación
      break;
    case ESTADO_PRESENTACION :
      // Espera que transcurra el tiempo de presentación para luego pasar a calentar
      if ((instanteActual - instanteAnterior) >= 5000UL) {        // Si ha transcurrido el tiempo deseado
        estadoLCD = ESTADO_LCD_PREPARAR;                          // Le dice a la máquina de estados del LCD que muestre los datos "estáticos"
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador
        if (CANTIDAD_CICLOS == 0) {
          estado = ESTADO_CALENTANDO_FINAL;   // Si no hay ciclos intermedios, entonces pasa directamente al calentamiento final
        }
        else {                                // Si hay ciclos
          ciclo = 1;                          // Está en el primer ciclo
          estado = ESTADO_CALENTANDO_CICLOS;   // Pasa a calentar un ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_CICLOS :
      // Está calentando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= MAXIMO_CALENTANDO) {                   // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_CICLOS;                        // Cambia al estado de enfriamiento del ciclo
      }
      break;
    case ESTADO_ENFRIANDO_CICLOS :
      // Está enfriando durante uno de los ciclos intermedios
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO) {                      // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador
        if (ciclo >= CANTIDAD_CICLOS) {                           // Si estaba en el último ciclo
          estado = ESTADO_CALENTANDO_FINAL;                       // Se pasa a el calentamiento final
        }
        else {                                                    // Si no estaba en el último ciclo
          ciclo++;                                                // Se incrementa el contador de ciclos (el famoso for)
          estado = ESTADO_CALENTANDO_CICLOS;                       // Se vuleve a calentar del ciclo
        }
      }
      break;
    case ESTADO_CALENTANDO_FINAL :
      // Está en el calentantamiento del final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura >= temperaturaCalentamientoFinal) {       // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        estado = ESTADO_ENFRIANDO_FINAL;                        // Pasa al enfriamiento final
      }
      break;
    case ESTADO_ENFRIANDO_FINAL :
      // Está en el enfriado final
      if (temperaturaErronea) {             // Si la temperatura es errónea
        estadoGuardado = estado;            // Guarada el estado actual
        estado = ESTADO_ERROR_TEMPERATURA;  // Pasa al estado de error de temperatura
        break;                              // Se sale del switch
      }
      if (temperatura <= MINIMO_ENFRIANDO_FINAL) {              // Si se ha alcanzado la temperatura deseada
        digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);  // Apaga el calentador
        digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);  // Apaga el ventilador
        estado = ESTADO_FINAL;                                  // Cambia al estado de finalización del proceso
      }
      break;
    case ESTADO_ERROR_TEMPERATURA :
      // Se ha detectado un error en el sensor de temperatura
      // Nos aseguramos de que se apaga el calentador y el ventilador
      digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_APAGADO);    // Apaga el calentador
      digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_APAGADO);    // Apaga el ventilador
      estado = ESTADO_PAUSA_ERROR_TEMPERATURA;                  // Pasa a esperar a que vuelva a haber una lectura correcta de la temperatura
      break;
    case ESTADO_PAUSA_ERROR_TEMPERATURA :
      // Espera a que se quite el error del sensor de temperatura
      if (!temperaturaErronea) {          // Si la temperatura no es errónea (es correcta)
        estado = estadoGuardado;          // Recupera el estado guardado de la máquina de estado
        switch (estado) {
          case ESTADO_CALENTANDO_CICLOS :
          case ESTADO_CALENTANDO_FINAL :
            digitalWrite(PIN_RELE_CALENTADOR, VALOR_RELE_ENCENDIDO);  // Enciende el calentador sólo para los estados ESTADO_CALENTANDO_CICLO y ESTADO_CALENTANDO_FINAL
          case ESTADO_ENFRIANDO_CICLOS :
          case ESTADO_ENFRIANDO_FINAL :
            digitalWrite(PIN_RELE_VENTILADOR, VALOR_RELE_ENCENDIDO);  // Enciende el ventilador para los cuatro estados
            break;
          default :
            // No hace enada para todos los demás estados
            break;
        }
      }
      break;
    case ESTADO_FINAL :
      // Ha finalizado todo el proceso
      // No hace nada
      break;
  }

  // Control de lo que se muestra en el LCD ayudado por una máquina de estados propia
  // .. y consultando el estado de la máquina de estado principal
  switch (estadoLCD) {
    case ESTADO_LCD_OCIOSO :
      // No ha de hacer nada con el LCD, más que dejarlo tal cual está
      // No hace nada
      break;
    case ESTADO_LCD_PRESENTACION :
      // Se muestra la presentación y pasa al estado ESTADO_LCD_OCIOSO
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(F("DESHIDRATADORA")); // Mensaje a desplegar
      lcd.setCursor(0, 1);
      lcd.print(F(" PERA 5 M.M. ")); // Mensaje 2 a desplegar
      estadoLCD = ESTADO_LCD_OCIOSO;      // Pasa a no hacer nada, hasta que la máquina de estados principal le diga que haga algo
      break;
    case ESTADO_LCD_PREPARAR :
      // "Prepara" la pantalla de trabajo, mostrando los valores definidos y que permanecen estáticos
      lcd.clear();
      if (CANTIDAD_CICLOS != 0) {
        // Si hay ciclos, se muestra las temperaturas de los ciclos
        lcd.setCursor(1, 2);
        lcd.print(MAXIMO_CALENTANDO);
        lcd.write(223);
        lcd.print('/');
        lcd.print(MINIMO_ENFRIANDO);
        lcd.write(223);
      }
      // Se muestran las temperaturas finales
      lcd.setCursor(1, 3);
      lcd.print(temperaturaCalentamientoFinal); // El valor de esta temperatura se ha calculado según el conmutador selector
      lcd.write(223);
      lcd.print('/');
      lcd.print(MINIMO_ENFRIANDO_FINAL);
      lcd.write(223);
      estadoLCD = ESTADO_LCD_TRABAJANDO;        // Pasa al estado de "trabajo", donde muestra el resto de valores
    // No ponemos break ya que queremos que realice las tareas del estado ESTADO_LCD_TRABAJANDO que ha de tratarse a continuación
    case ESTADO_LCD_TRABAJANDO :
      // Muestra y actualiza la información
      { // Creamos un bloque de código dentro de este case porque las siguientes variables sólo van a ser usadas en este case
        bool mostrarPasos = false;    // Tan sólo es para saber si ha de actualizar por qué paso va, ya que está dentro de un ciclo
        int temperaturaDestino = -1;  // Temperatura que se quiere alcanzar. Inicializada a -1 por si no se le asigna valor, que tenga algo que mostrar
        char subeBaja = ' ';          // Carárter animado que indica si se está calentando o enfriando
        char calientaCiclo = ' ';     // Carárter animado que se muestra junto a la temperatura máxima del ciclo
        char enfriaCiclo = ' ';       // Carárter animado que se muestra junto a la temperatura mínima del ciclo
        char calientaFinal = ' ';     // Carárter animado que se muestra junto a la temperatura máxima final
        char enfriaFinal = ' ';       // Carárter animado que se muestra junto a la temperatura mínima final
        lcd.setCursor(1, 0);          // Posiciona el cursor para poner qué es lo que está haciendo
        // Aprovechamos la máquina de estados «principal» para saber qué hemos de mostrar
        switch (estado) {
          case ESTADO_INICIAL :
          case ESTADO_PRESENTACION :
            // No hace nada. Es más, no debería de darse nunca esta condición
            break;
          case ESTADO_CALENTANDO_CICLOS :
            lcd.print(F("CALENTANDO"));             // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;           // Animación de calentar
            calientaCiclo = CARACTER_CALENTAR;      // Animación de calentar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MAXIMO_CALENTANDO; // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ENFRIANDO_CICLOS :
            lcd.print(F("ENFRIANDO "));             // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;            // Animación de enfriar
            enfriaCiclo = CARACTER_ENFRIAR;         // Animación de enfriar
            mostrarPasos = true;                    // Ha de actualizar la información de por qué paso va
            temperaturaDestino = MINIMO_ENFRIANDO;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_CALENTANDO_FINAL :
            lcd.print(F("CALENTANDO"));                         // Pone qué está haciendo
            subeBaja = CARACTER_CALENTAR;                       // Animación de calentar
            calientaFinal = CARACTER_CALENTAR;                  // Animación de calentar
            temperaturaDestino = temperaturaCalentamientoFinal; // Temperatura que se ha de alcanzar, que depende el conmutador de selección
            break;
          case ESTADO_ENFRIANDO_FINAL :
            lcd.print(F("ENFRIANDO "));                   // Pone qué está haciendo
            subeBaja = CARACTER_ENFRIAR;                  // Animación de enfriar
            enfriaFinal = CARACTER_ENFRIAR;               // Animación de enfriar
            temperaturaDestino = MINIMO_ENFRIANDO_FINAL;  // Temperatura que se ha de alcanzar
            break;
          case ESTADO_ERROR_TEMPERATURA :       // Tanto en el estado ESTADO_ERROR_TEMPERATURA...
          case ESTADO_PAUSA_ERROR_TEMPERATURA : // ... como en el estado ESTADO_PAUSA_ERROR_TEMPERATURA
            if ((instanteActual / 500) & 1) {   // Se muestra parpadeando el mensaje de aviso
              lcd.print(F("ERROR"));
            }
            else {
              lcd.print(F("          "));

            }
            // Se va a mostrar la temperatura destino dependiendo del estado en que estaba la máquina de estado principal cuando ha fallado la temperatura
            switch (estadoGuardado) {
              case ESTADO_CALENTANDO_CICLOS :
                temperaturaDestino = MAXIMO_CALENTANDO;
                break;
              case ESTADO_CALENTANDO_FINAL :
                temperaturaDestino = temperaturaCalentamientoFinal;
                break;
              case ESTADO_ENFRIANDO_CICLOS :
                temperaturaDestino = MINIMO_ENFRIANDO;
                break;
              case ESTADO_ENFRIANDO_FINAL :
                temperaturaDestino = MINIMO_ENFRIANDO_FINAL;
              default :
                break;
            }
            break;
          case ESTADO_FINAL :
            // La máquina de estados principal ha finalizado
            estadoLCD = ESTADO_LCD_MUESTRA_FINAL; // La máquina de estados del LCD pasa a mostrar el mensaje final
            break;
        }
        // Muestra por qué ciclo va
        if (mostrarPasos) {
          lcd.setCursor(3 - (ciclo / 10), 1);
          lcd.print(ciclo);
          lcd.print('/');
          lcd.print(CANTIDAD_CICLOS);
          lcd.print('x');
        }
        // Muestra la temperatura destino
        lcd.setCursor(14, 1);
        lcd.print(temperaturaDestino);
        lcd.write(223);
        lcd.write(subeBaja);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(0, 2);
        lcd.write(calientaCiclo);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 2);
        lcd.write(enfriaCiclo);
        // Muestra la temperatura máxima final (que puede variar)
        lcd.setCursor(0, 3);
        lcd.write(calientaFinal);
        lcd.print(temperaturaCalentamientoFinal);
        // Muestra la animación de la temperatura máxima del ciclo
        lcd.setCursor(8, 3);
        lcd.write(enfriaFinal);
      }
    case ESTADO_LCD_TEMPERATURA :
      // Muestra actualizado el valor de la temperatura
      lcd.setCursor(12, 0);
      if (temperaturaErronea) {
        lcd.print(F("PELIGRO")); // Es lo que muestra si la lectura de temperatura no es correcta
      }
      else {
        if ((temperatura < 100) && (temperatura > -10)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        if ((temperatura < 10) && (temperatura >= 0)) {
          lcd.write(' ');       // Pone el espacio para alinear la temperatura a la derecha
        }
        lcd.print(temperatura);
      }
      lcd.write(223);
      break;
    case ESTADO_LCD_MUESTRA_FINAL :
      // Muestra el mensaje de finalización y pasa al estado ESTADO_LCD_TEMPERATURA
      lcd.clear();
      lcd.setCursor(3, 2);
      lcd.print(F("FIN DE PROCESO"));
      estadoLCD = ESTADO_LCD_TEMPERATURA;
      break;
  }

}

void botonera() {
  switch (estado) {
    case ESTADO_BOTON_INICIA_MENU :
      if (boton_menu, LOW);
      lcd.setCursor(1, 3);
      lcd.print("menu");
      Serial.print("menu");
      lcd.print(boton_menu);
      Serial.print(boton_menu);
      break;

    case ESTADO_BOTON_ENTRAR_MENU1 :
      if (boton_menu, LOW);
      lcd.setCursor(12, 3);
      char *menu1[] = {"ciclo ", "ciclo_sube ", "ciclo_baja", "final_sube", "final_baja", " fin "};
      lcd.println("menu1");
      Serial.println("menu1");
      break;

    case ESTADO_BOTON_CALENTANDO_CICLO :
      if (digitalRead, boton_sube == LOW) {
        //  CANTIDAD_CICLOS ++;
      }
      break;
  }
}

Espero esta mas cerca de la solución.
Saludos.

hola @victorjam, creo que hay un error que puede estar causando el problema

 switch (estado ) {

Nota: En los renglones 553 y 671 esta repetido y no logro singularizar los void boton, void menu etc y nada.

Tu principal problema es que en algun momento habras cambiado la variable de la máquina de estados.
la variable estado la usas para los botones y tmb la tienes en

// Control de la máquina de estados "principal"
  switch (estado) { // linea 396

en 484

  case ESTADO_PAUSA_ERROR_TEMPERATURA:
    // Espera a que se quite el error del sensor de temperatura
    if (!temperaturaErronea) {          // Si la temperatura no es errónea (es correcta)
      estado = estadoGuardado;          // Recupera el estado guardado de la máquina de estado
      switch (estado) { // linea 484

en 553

 // Aprovechamos la máquina de estados «principal» para saber qué hemos de mostrar
      switch (estado) { // linea 553

Asi que separa nombres de máquinas de estado.

Me he leido el código que posteaste al principio, supongo que es el original de @IgnoranteAbsoluto, aunque me vendría bien saber de que post viene, para entender el código completo.

Lo siguiente que me viene a la cabeza es qué parámetros quieres editar: minima, maxima, ciclos, etc. Ten en cuenta que en el código original todo eso son constantes y no se pueden modificar.

La máquina de estados del calentamiento/enfriamiento mas o menos la entiendo y creo que no debes tocarla si funciona a no ser que quieras mejorarla.

La maquina de estados de estadoLCD solo muestra información y es la que hay que modificar para atender las peticiones del menu, lo suyo será hacer estados nuevos para interactuar con los botones y poner nuevas transiciones desde las que ya hay.

Te recomiento que no uses el código de la libreria de los botones dentro del programa, ya es suficientemente largo y lo vamos a empeorar, descargatelo de github e instala la libreria.

Incluye la libreria en el programa:

#include <BotonSimple.h>

Crea los botones:

BotonSimple nombreBoton(pin_del_boton);
BotonSimple otroBoton(pin_del_boton);

Para que el boton funcione correctamente debes actualizar el estado. En el loop, antes de hacer nada hay que llamar a la función actualizar.

void loop() {
  nombreBoton.actualizar();
  otroBoton.actualizar();
}

En cuanto hayas conseguido instalar la libreria de los botones y me digas que parametro son los que quieres editar veremos como encajarlo en la máquina de estados de la pantalla.

Hola @victorjam, en esta liga esta el código original, al que yo le quería agregar los ciclo for, Salir y continuar de blucle for - #27 by gonpezzi la @gonpezzi me ayudo a integrar el ciclo for, al código original y funciona bien, luego @IgnoranteAbsoluto propuso la maquina de estados y fue mucho mas fácil modificar los parámetros de ciclos y temperatura y tiene una alarma que lo hace que sea muy seguro al apagar el equipo en caso de emergencia. Pero ahora veo que seria mas fácil implementar botones al que creo @gonpezzi. fue @IgnoranteAbsoluto el que propuso agregar los botones en un nuevo tema, cosa que ya no se realizo, yo e querido quitar los caracteres de las flechas que consumen muchos renglones como 200, intentando hacer ingeniería inversa para usar el código en otros proyectos, y nunca lo e conseguido. voy a cargar la librería #include <BotonSimple.h> y poner 4 botones a un que creo con 3 seria suficiente seria menu, aumentar y disminuir y salir si no se oprime ninguna tecla en 30 segundos.

const byte CANTIDAD_CICLOS  = 12;                        // Cantidad de ciclos intermedios que se quiere
const int MAXIMO_CALENTANDO = 58.0;                     // Temperatura hasta que se ha de calentar en los ciclos intermedios
const int MINIMO_ENFRIANDO  = 48.0;                     // Temperatura hasta que se ha de enfriar en los ciclos intermedios
const int MAXIMO_CALENTANDO_FINAL_A = 60.0;             // Temperatura hasta que se ha de calentar al final (opción A)
const int MAXIMO_CALENTANDO_FINAL_B = 70.0;             // Temperatura hasta que se ha de calentar al final (opción B)
const int MINIMO_ENFRIANDO_FINAL    = 36.0;             // Temperatura hasta que se ha de enfriar para apagar el ventilador

Explicacion de Ignorante Absoluto
el primer parametro seria el numero de ciclos y luego el maximo y el minimo de el ciclo for luego maximo final minimo final que es un "operador ternario" y minimo enfriando que tiene que ser comparado con la temperatura ambiental para que el al envasar el producto haga cierto vacio el empaque te agrego la explicación de @IgnoranteAbsoluto. de como funciona su código.

  • CANTIDAD_CICLOS determina el número de veces que se han de alcanzar la temperatura máxima y mínima de los ciclos antes de pasar a alcanzar la temperatura máxima final. Puede ser cualquier valor entre cero y 99. Si es cero, no aparecerá en el LCD ni el número de ciclos ni los valores de las temperaturas máxima y mínima de los ciclos. Tan sólo aparecerá la temperatura máxima final y la temperatura mínima a la que se ha de apagar.
  • MAXIMO_CALENTANDO es la temperatura máxima que se ha de alcanzar en los ciclos al calentar.
  • MINIMO_ENFRIANDO es la temperatura mínima que se ha de alcanzar en los ciclos en el enfriado.
  • MAXIMO_CALENTANDO_FINAL_A y MAXIMO_CALENTANDO_FINAL_B son las dos temperaturas que se pueden seleccionar con el conmutador a la que ha de llegar en el calentamiento final.
  • MINIMO_ENFRIANDO_FINAL es la temperatura a la que se ha de llegar para finalizar el proceso y apagar el ventilador en el enfriamiento final.
  • INTERVALO_ENTRE_LECTURAS indica cada cuántos milisegundos se ha de tomar una muestra de temperatura. Es aconsejable ponerle el sufijo UL a ese número, para indicarle al compilador que es unsigned long porque por defecto trata los literales numéricos como int y nos podríamos llevar una sorpresa si la cantidad de milisegundos fuera mayor a 65535. Supongo que no será este el caso, pero es bueno acostumbrarse a ponerlo.
  • FLECHAS los caracteres animados que indican si se está calentando o enfriando serán otros dependiendo de si se indica un true o un false en la definición de esta constante. Prueba a cambiar el valor y ya me dirás si finalmente has dejado las flechas o has preferido «los otros». Siempre podrás cambiar el código y definir otras animaciones. Lo que está puesto a fuego que son 4 los «pasos» de cada animación.

Lo creas o no, esta es la versión «rápida» y sin muchas florituras (aunque algunas hay). Creo que la versión «buena» debería de permitir que se pudieran configurar las temperaturas desde el mismo Arduino, sin tener que estar modificando, compilando y cargando el programa al Arduino cada vez que vas a cambiar las temperaturas. Yo quitaría el conmutador que tienes para seleccionar entre las dos temperaturas máximas finales y le pondría tres pulsadores. Uno para incrementar el valor de cada temperatura, otro para decrementar y un tercero para «avanzar» a la siguiente temperatura. No hace falta más, ni tan siquiera para «volver» a la anterior. Bastaría con pulsar varias veces hasta «pasar» por todas y «volver» a la primera cíclicamente. Pero si se quiere se pueden poner más pulsadores con más funcionalidades. Quien dice que son para configurar las temperaturas, también sirven para cambiar el número de ciclos a realizar. Así como para indicar que empiece el proceso una vez configurado o que lo interrumpa por si se quiere empezar de nuevo o cambiar algún parámetro. Y ya, para hacerlo más cómodo aún, que pueda guardar en la EEPROM al menos los últimos valores configurados para tenerlos ya al encender el Arduino. Aunque yo no me quedaría ahí, sino que también se puedan guardar varias configuraciones distintas en la EEPROM.

Hola @Surbyte, me pase ayer toda la tarde intentando separar los nombres, y no pude mejor termine creando un nuevo void botonera, si me das una idea de como puedo hacerlo, por que el código no reconoce el nuevo void los botones tienen que quedar dentro del void loop para que sea parte de la maquina de estados.

enum estado_botones_t {
  ESTADO_BOTON_INICIA_MENU,                 
  ESTADO_BOTON_ENTRAR_MENU1,            
  ESTADO_BOTON_CALENTANDO_CICLO,       
  ESTADO_BOTON_ENFRIANDO_CICLO,         
  ESTADO_BOTON_CALENTANDO_FINAL,        
  ESTADO_BOTON_ENFRIANDO_FINAL,
  

Este es el enum que yo agregue pero cuando paso al switch, case, break ya no puedo hacer que copile A un que creo que el unum debería de ser así

enum estado_botones_t {
  ESTADO_BOTON_INICIA_MENU,                
  ESTADO_BOTON_CANTIDAD_CICLOS_INCREMENTA,
 ESTADO_BOTON_CANTIDAD_CICLOS_DECREMENTA            
  ESTADO_BOTON_CALENTANDO_INCREMENTA,
  ESTADO_BOTON_ENFRIANDO_DECREMENTA, 
  ESTADO_BOTON_CALENTANDO_FINAL_INCREMENTA,   
  ESTADO_BOTON_ENFRIANDO_FINAL_DECREMENTA,
  ESTADO_BOTON_FINAL_INCREMENTA,
  ESTADO_BOTON_FINAL_DECREMENTA
};

Creo que debería de ser así

me da error al agregar #include <BotonSimple.h> y si comento el renglón 1 si copila y funciona no pude incluir la librería via zip la cargue manual mente


//#include <BotonSimple.h>
#define APRETADO    0
#define SUELTO      1
#define APRETANDOLO 2
#define SOLTANDOLO  3
class BotonSimple {
    private:
    unsigned char pin;
    unsigned char anterior, valor;
    unsigned char estado;

    unsigned long temporizador;
    unsigned long tiempoRebote;
  public:
    BotonSimple(unsigned char _pin, unsigned long _tiempoRebote);
    void actualizar();
    int  leer();
};

BotonSimple::BotonSimple(unsigned char _pin, unsigned long _tiempoRebote = 50) {
  pin = _pin;
  tiempoRebote = _tiempoRebote;
  pinMode(pin, INPUT_PULLUP);
  valor = HIGH; anterior = HIGH; estado = SUELTO;
}

void BotonSimple::actualizar() {
  // NOTA: En el ejemplo original en vez de "pin" leia directamente el pin "2", con lo que el
  // codigo no funciona correctamente con el pin que le hayamos asignado.
  if ( valor == digitalRead(pin) ) {
    temporizador = 0;
  }
  else if ( temporizador == 0 ) {
    temporizador = millis();
  }
  else if ( millis() - temporizador >= tiempoRebote ) {
    valor = !valor;
  }
  if ( anterior == LOW  && valor == LOW  ) estado = APRETADO;
  if ( anterior == LOW  && valor == HIGH ) estado = SOLTANDOLO;
  if ( anterior == HIGH && valor == LOW  ) estado = APRETANDOLO;
  if ( anterior == HIGH && valor == HIGH ) estado = SUELTO;
  anterior = valor;
}

int BotonSimple::leer() {
  return estado;
}


BotonSimple boton(8);

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  boton.actualizar();
  switch ( boton.leer() ) {
    case SUELTO: digitalWrite(13, LOW); break;
    case APRETANDOLO: Serial.println("Acabas de apretar el boton"); break;
    case APRETADO: digitalWrite(13, HIGH); break;
    case SOLTANDOLO: Serial.println("Acabas de soltar el boton"); break;
    default: break;
  }
}

Saludos.

Hola foro, después de mucho discernir, de repensar. el código de Ignorante Absoluto, por sugerencia de Victor Jaram, y estudiar la forma de separa los switch,case por consejo de Surbyte, creo que esta es la mejor forma (que yo creo) de que esto pueda funcionar con la ayuda de ustedes.


enum estado_botones_t {
  ESTADO_BOTON_INICIA_MENU,
  ESTADO_BOTON_CANTIDAD_CICLOS,
  ESTADO_BOTON_CALENTANDO_ENFRIANDO,
  ESTADO_BOTON_CALENTANDO_FINAL_ENFRIANDO_FINAL,
  ESTADO_BOTON_FRIO_INCREMENTA_DECREMENTA,
};
 }


  switch (estado_boton_t ) {
    case ESTADO_BOTON_INICIA_MENU :                // menu debe tener 4 case diferentes ciclos, calentando, calentando final, frio
      if ( digitalRead(boton_menu) == HIGH )
        MENU ++ ;
      break;
  }
case   ESTADO_BOTON_CANTIDAD_CICLOS, :              // este valor deberia tener un tope maximo de 25 ciclos y 0 ciclos como minimo
  if ( digitalRead(boton_sube) == HIGH ) {
    CANTIDAD_CICLOS ++ ;
    if ( digitalRead(boton_baja) == HIGH )
      CANTIDAD_CICLOS -- ;

    break;
  }
case   ESTADO_BOTON_CALENTANDO_ENFRIANDO :          //esta variable debria tener n tope maximo de 75° 40° de minimo
  if ( digitalRead(boton_sube) == LOW ) {
    MAXIMO_CALENTANDO ++ ;
  }
  if ( digitalRead(boton_baja) == HIGH ) {
    MINIMO_ENFRIANDO -- ;
  }
  break;
case ESTADO_BOTON_CALENTANDO_FINAL_ENFRIANDO_FINAL : // este parametro deberia tener un topo maximo de 85° y un tope minimo de 55°
  if ( digitalRead(boton_sube) == LOW ) {
    MAXIMO_CALENTANDO_FINAL ++ ;
  }
  if ( digitalRead(boton_baja) == HIGH ) {
    MINIMO_ENFRIANDO_FINAL -- ;
  }
  break;
case   ESTADO_BOTON_FRIO_INCREMENTA_DECREMENTA :    //esta variable deberia tener un tope maximo de 40° y un minimo de 5°
  if ( digitalRead(boton_sube) == LOW ) {
    MAXIMO_FRIO ++ ;

    if ( digitalRead(boton_baja) == HIGH )
      MINIMO_FRIO-- ;
    break;
  }

}

Esto es lo que le agregue al codigo. enum switch y 5 case y creo es lo mas simplificado si queremos que siga siendo maquina de estados y si vemos que con este código no es posible (yo creo que si) lo intentamos con el de @gonpezzi