Control horno eléctrico LCD + MAX6675 + TIMER

UN SALUDO PARA TODOS

he conseguido un código en la red de un timer , lo he editado y adaptado para usarlo con mi horno eléctrico utilizando un max 6675 + sensor , llegue hasta donde pude adaptarlo , si alguien un compañero me pudiera orientar para agregarle 2 funciones mas

  1. un botón extra para parar el proceso o stop/ reset .
  2. colocarle una funcio que me controle un rele/ssr o actuador on/off para cuando compare con el setpoint apague la resistencia y no deje calentar mas de la temperatura seteada

comparto el código hasta donde pude llegar

#include <LiquidCrystal_I2C.h>
#include <max6675.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);


//----------------------------------TEMPE
//int thermoDO = 4;
//int thermoCS = 5;
//int thermoCLK = 6;

int ktcSO = 4;
int ktcCS = 5;
int ktcCLK = 6;

//MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
MAX6675 ktc(ktcCLK, ktcCS, ktcSO);

#define   ST_TEMP   A0
#define   STOP      A1

float temperatura = 0;
unsigned int setpoint_temp;
char buffer[16];

int ssr = 12;               //Control con rele
int ahoras = 0;            //Variable a mostrar por LCD de las horas
int aminutos = 0;          //Variable a mostrar por LCD de los minutos
int asegundos = 0;         //Variable a mostrar por LCD de los segundos
int segundostotal = 0;     //Tiempo total
int msg = 0;               //Barrera para el mensaje de bienvenida

int start = A2;            //Pulsador de arranque


int empieza = 1024;        // Variable para almacenaje del pulsador de arranque

int buth = A5;             //Pulsador de Horas
int butm = A4;             //Pulsador de Minutos
int buts = A3;             //Pulsador de segundos

int varbuth = 0;           //Variable para almacenar el valor del pulsador de horas
int varbutm = 0;           //Variable para almacenar el valor del pulsador de minutos
int varbuts = 0;           //Variable para almacenar el valor del pulsador de segundos

byte reloj[] = {
  0x00,
  0x0E,
  0x15,
  0x15,
  0x1D,
  0x11,
  0x0E,
  0x00
};

byte DF[] = {
  0x00,
  0x00,
  0x00,
  0x1F,
  0x0E,
  0x04,
  0x00,
  0x00
};


byte termo[] = {
  B00100,
  B01010,
  B01010,
  B01110,
  B01110,
  B11111,
  B11111,
  B01110
};


byte carita[] = {
  0x00,
  0x0A,
  0x04,
  0x00,
  0x11,
  0x0E,
  0x00,
  0x00
};





void setup()
{
  Serial.begin(9600);          //  setup serial
  lcd.init();
  lcd.backlight();

  pinMode(ssr, OUTPUT);     //Pin de alarma --> Salida
  pinMode(buth, INPUT);     //Pin de pulsador de horas --> Entrada
  pinMode(butm, INPUT);     //Pin de pulsador de minutos --> Entrada
  pinMode(buts, INPUT);     //Pin de pulsador de segundos --> Entrada
  pinMode(start, INPUT);    //Pin de pulsador de arranque --> Entrada

  msg = 0;                  //Barrera del mensaje de bienvenida
  empieza = 1024;           //Barrera de arranque
  set_temp ();

  varbuth = 1;              //Barrera de horas
  varbutm = 1;              //Barrera de minutos
  varbuts = 1;              //Barrera de segundos

  lcd.createChar(0, reloj);
  lcd.createChar(1, DF);
  lcd.createChar(2, termo);
  lcd.createChar(3, carita);

}


void loop()
{
  if (msg == 0)          //Mostramos el mensaje de bienvenida solo una vez

  {
    lcd.setCursor(2, 0);
    lcd.print("CONTROL DE    ");
    lcd.setCursor(3, 1);
    lcd.print("TEMPERATURA");
    delay(2500);
    msg = 1;
    lcd.clear();

  }



  //-------------------------------------------------------------------------------------------------
  // LECTURA DE LOS BOTONES Y ELECCIÓN DEL TIEMPO, NO SALE DEL BUCLE HASTA PULSAR
  // EL BOTON DE ARRANQUE
  //-------------------------------------------------------------------------------------------------

  do

  {

    varbuth = analogRead(buth);   //Leemos boton de horas
    varbutm = analogRead(butm);   //Leemos boton de minutos
    varbuts = analogRead(buts);   //Leemos boton de segundos

    if (varbuth == 0)             //Si el boton ha sido pulsado, aumentamos las horas en una unidad
    {
      ahoras = ahoras + 1 ;
      delay(250);
    }

    if (varbutm == 0)           //Si el boton ha sido pulsado, aumentamos los minutos en una unidad
    {
      aminutos = aminutos + 1;
      delay(250);
    }

    if (varbuts == 0)           //Si el boton ha sido pulsado, aumentamos los segundos en una unidad
    {
      asegundos = asegundos + 1;
      delay(250);
    }

    set_temp ();

    //-----------------------------------------------------------

    lcd.setCursor(0, 1);  //---------reloj
    lcd.write((byte)0);

    lcd.setCursor(2, 1);
    lcd.print("TIMER");  //Muestra mensaje y las HH:MM:SS que vayamos aumentando

    lcd.setCursor(8, 1);
    if (ahoras < 10) lcd.print("0");    // Si las horas son menor que 10, pone un "0" delante.
    lcd.print(ahoras);                 // Sin este codigo, se muestra asi: H:M:S  (1:M:S)
    lcd.print(":");

    if (aminutos < 10) lcd.print("0");  // Si los minutos son menor que 10, pone un "0" delante.
    lcd.print(aminutos);               // Sin este codigo, se muestra asi: H:M:S  (H:1:S)

    lcd.print(":");
    if (asegundos < 10) lcd.print("0"); // Si los segundos son menor que 10, pone un "0" delante.
    lcd.print(asegundos);              // Sin este codigo, se muestra asi: H:M:S  (H:M:1)

    empieza = analogRead(start);   //Lee el boton de arranque

    if (empieza == 0)              //Si el boton de arranque, fue pulsado...  //

    {

      segundostotal = asegundos + (aminutos * 60) + (ahoras * 60 * 60);  //Convierte el tiempo elegido en segundos!!

    }

  }


  while (empieza != 0); // Se repite el menu de elegir tiempo hasta que pulsemos el boton de arranque.

  //-------------------------------------------------------------------------------------------------
  // UNA VEZ PULSADO EL BOTON DE ARRANQUE Y ACUMULADO EL TIEMPO, ENTRA EN EL SIGUIENTE WHILE
  // Y NO FINALIZA HASTA TERMINAR LA CUENTA.
  //-------------------------------------------------------------------------------------------------

  while (segundostotal > 0)
  {
    display_temp();


    delay (1000);          //Descontamos en periodos de 1 segundo
    segundostotal--;
    ahoras = ((segundostotal / 60) / 60);  //Convertimos los segundos totales en horas
    aminutos = (segundostotal / 60) % 60;  //Convertimos los segundos totales en minutos
    asegundos = segundostotal % 60;        //Convertimos los segundos totales en periodos de 60 segundos



    lcd.setCursor(0, 0); //-------TERMO
    lcd.write((byte)2);

    lcd.setCursor(2, 0);
    lcd.print("TEMP ");        //Mostramos mensaje de tiempo restante

    lcd.setCursor(0, 1); //-------reloj
    lcd.write((byte)0);

    lcd.setCursor(2, 1);
    lcd.print("TIMER");        //Mostramos mensaje de tiempo restante

    lcd.setCursor(8, 1);
    if (ahoras < 10) lcd.print("0");     // Si las horas son menor que 10, pone un "0" delante.
    lcd.print(ahoras);                   // Sin este codigo, se muestra asi: H:M:S  (1:M:S)
    lcd.print(":");

    if (aminutos < 10) lcd.print("0");   // Si los minutos son menor que 10, pone un "0" delante.
    lcd.print(aminutos);                 // Sin este codigo, se muestra asi: H:M:S  (H:1:S)

    lcd.print(":");
    if (asegundos < 10) lcd.print("0");  // si el valor de segundo esta por debajo de 9 (unidad) antepone un cero
    lcd.print(asegundos);                // Sin este codigo, se muestra asi: H:M:S  (H:M:1)

    if (segundostotal == 0)            //Si finaliza el tiempo

    {

      while (1)  //Bucle infinito mostrando mensaje y haciendo parpadear un led

      {

        lcd.clear();
        lcd.setCursor(4, 0);
        lcd.print("HORNEADO");
        lcd.setCursor(3, 1);
        lcd.print("  FELIZ   ");
        digitalWrite(ssr, LOW);
        lcd.setCursor(11, 1); //-------carita
        lcd.write((byte)3);
        delay(5000);
        lcd.clear();
        return ;
      }
    }
  }

}

void display_temp() {

  temperatura = ktc.readCelsius();
  lcd.setCursor(9, 0);
  lcd.print( temperatura);
  lcd.setCursor(13, 0);
  lcd.print((char)223);
  lcd.setCursor(14, 0);
  lcd.print("C");
  digitalWrite(ssr, HIGH);

}

void display_temp(int cache_temp)
{
  lcd.setCursor(0, 0);
  sprintf(buffer, "  TEMP    %u%cC    ", cache_temp, 0xDF);
  lcd.print(buffer);
}

void display_temp_loop(int cache_temp_set, int cache_temp_actual)
{
  lcd.setCursor(0, 0);
  sprintf(buffer, "S:%d%cC  ACT:%u%cC", cache_temp_set, 0xDF, cache_temp_actual, 0xDF);
  lcd.print(buffer);


}

void set_temp () {

  double setpoint_temp  = analogRead( ST_TEMP) * 0.2151;
  display_temp(setpoint_temp);

}

Resumiendo, encuentras un programa en Internet.
Vienes lo pegas aquí. Luego planteas que mas quieres que haga el código que no satisface tus necesidades.
Yo diría que no te has esforzado muy poco salvo por el hecho de buscarlo y punto.
Cuando hablo de esfuerzo me refiero a aprender a programar, me parece que del modo que lo planteas chocarás siempre con problemas similares.

En lo personal luego de analizar el código diré que no me gusta. Que raro no?
Lo primero que no me gusta es como implementa las secuencias. Esos do {} while interminables y ni hablar de los delay(250) que estan por todos lados.
Para terminar un reloj armado con delay() cuando en tu propio arduino cuentas con un reloj con precisión milisegundos llamado millis().

Reformarlo es un trabajo. Si tienes paciencia te intruduzco en la tarea pero eso si te esfuerzas, de lo contrario dejaré que alguien mas te brinde el apoyo que necesitas.

De todos modos.

  1. El boton de stop/reset es solo un pulsador que debiera ser consultado en todo momento en el que se te ocurra interrumpir la secuencia del programa. En este caso no se cuando vas a leerlo ya que este código no lo permite salvo que en cada fase del programa vuelvas a consultar por dicho pulsador.

Sobre el final de la secuencia de ajuste de tiempo tienes

empieza = analogRead(start);   //Lee el boton de arranque

Por qué usar analogRead() para leer un pulsador? Eso es típico de alguien que no sabe lo que esaba haciendo a nivel programación, por mas que luego funcione.
La instrucció para leer estados digitales es digitalRead(pin) analogRead es para leer valores analógicos entre 0-5V

Si el botón esta pulsado a GND y lees con analogRead puede que te marque 0 1 2 .... dependerá de que tan cerca este el botón y si los cables no son largos. En cambio con digitalRead() si leera 0.

Arduino tiene muchos pines digitales, usar los analógicos es un recurso si solo si, tienes empleados todos los demás.
Considera esto como crítica constructiva.
Si dispones de pines digitales 2 al 13 disponibles úsalos pero si no, usar los A0..A5 para el UNO no esta mal, pero puedes incluso asi, usarlos como digitales.

Tanto he dicho y no te aconsejé nada que te resulte práctico o si lo hice pero no sé si lo comprenderás.
Luego de

 while (segundostotal > 0)  {
    display_temp();

pregunta por un boton stop = digitalRead(pinStop); // define dicho boton y la variable stop.

en tu caso eso debería resolverse llevando todo al momento inicial o bien apagando el accionar de la resistencia calefactora etc, etc.

  1. La funcion que controle un SSR (Solid state Relay) no es problema.
    Si sabes tu temperatura actual medida con el MAX y conoces tu setpoint ya indicado por menú solo debes controlar el encendido apagado del SSR con algo similar a esto
if (temperatura > setpoint) {
    digitalWrite(pinSSR, LOW);      // apago el SSR y con ello se apaga la resistencia calefactora.
}
else {
    digitalWrite(pinSSR, HIGH);      // enciendo el SSR y con ello se apaga la resistencia calefactora.
}

Normalmente si usaras un RELE en lugar de un SSR te hubiera aconsejado esto a diferencia de lo anterior

if (temperatura < (setpoint - Histeresis)) {
   digitalWrite(pinSSR, HIGH);      // enciendo el SSR y con ello se apaga la resistencia calefactora.
}

Histeresis es una constante float que puede valer algo como 0.5 por ejemplo o 1.0
Esto permite que en el umbral de cambio o sea cuando la temperatura de tu horno sea igual a la temperatura del setpoint no este oscilando (prendiendo/apagando algo que en un SSR no afecta pero en un RELAY si, afecta la vida útil de sus constactos).

Otro buen ejercicio si estas iniciando en esto, es hacer un diagrama de flujo de como quieres que se comporte el equipo y después programar.

También es muy útil aprender a programar los dispositivos como máquina de estados, programar con esa filosofía muy bueno al momento modificar el código, hace menos traumático si quieres agregar funciones adicionales, y por supuesto facilita la tarea de un dispositivo que tenga que cumplir muchas funciones, se programa cada estado para que haga determinadas funciones y no como un todo, solo hay que tener cuidado después que haga determinada tarea, a cual tarea siguiente (o estado) debe saltar.

Saludos y éxito.

Justamente la máquina de estados que comenta @soyega es la solución correcta pero requiere ponerse a estudiar/practicar y cambiar tu mente de como encarar estas cosas.
Si tomas un programa de alguien solo te vas a perder en cómo quiso hacerlo.
Lo mejor es tomar un ejemplo y tener claro como se debe encarar un programa.

Miras el ejemplo del MAX y el resto lo ensayas de otro modo.

ejemplo para tu timer tienes millis() como ya te dije.. con millis() ya tienes un cronómetro listo para usar.

Solo ajustas por teclado tu tiempo HH:MM:SS, almacenas, luego comparas. Listo.
No es fácil, lleva tiempo hacer que todo funcione.

Lo demás se va separando en acciones.
En tu caso fijate que el código lleva una variable msg que comienza en 0 bueno esa será tu variable de la máquina de estados.
La máquina de estados no USA DELAY() y mira como lo escribo, lo hago con mayúsculas, las mismas que vivo diciendo que no se deben usar, pero acá es para enfatizar lo que no se debe hacer en una máquiina de estados.

Entonces una máquina de estados es un switch(msg) {} donde cada case será el valor que tiene para uno u otro caso.

En tu caso comienzas con

case 0: // la presentación y pasas a 1 
            break;
case 1: // ajuste de tiempo y temperatura
           break;
case 2: // podria ser control de temperatura y tiempo
           break;

como tu código va mirando que hacer y devuelve en menos de 1 milisegundo
puede ver que teclas o botones pulsas entonces colocar algo que detenga es simple, colocar algo que vuelva a resetear es simple.

Se ve la idea?

Entendido gracias a todos ..