Ayuda para el primer gran reto de un dummy

Hola otra vez.
Al final en mi caso ha sido antes el huevo que la gallina. En una cena con amigos y amigos de amigos he encontrado una persona que le sacara utilidad a lo que tenía entre manos. Así, al final si que hay proyecto. Él ya tiene una instalación que le da una señal de salida al cumplirse unas determinadas condiciones, y lo que quiere es doblar esa señal a una fija y otra con tiempo variable. Para hacerlo he puesto un 3er pulsador que emula el pulso de la señal para iniciar actividad. Le he puesto un pequeño delay para los rebotes, solo para asegurarme de que si falla algo no sea de aquí el problema, pero lo eliminare antes de darlo por acabado. He aprovechado el código que me ofreció Marcial, lo he entendido y alterado para adaptarlo a mis necesidades. He implementado código para leer y escribir en la eeprom para no perder el valor del tiempo que hay fijado tal y como se me había sugerido. Al final ha quedado un sketch que si bien seguramente no será muy académico si que es funcional al 100% y cumple con lo que esperaba de el. Lo que no he sido capaz es de aplicar la solución para el desbordamiento de Millis(), y eso que me lo habían dado “mascado”. De donde no hay no se puede sacar.
Estaría muy agradecido si alguien me dice como (o me lo hace) aplicar la solución por si acaso. Gracias.

#include <LiquidCrystal.h>
#include <EEPROM.h>
byte direccion = 0;

LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);
unsigned long T_High=0;            // Milesimas de estado high
unsigned long T_Ciclo=30000;       // Milesimas del ciclo
unsigned long Milis_Ciclo=0;       // Para controlar el temporizador del ciclo
unsigned long Milis_High;        // Para controlar el temporizador de activo

// Los  botones
byte buttonPin = 2;
byte buttonPin_2 = 4;
byte buttonPin_3 = 6;
byte Salida = 5;
byte salida_2 = 13; 

// para button state change de cada boton
bool BtnSumar=false;
bool BtnDigito=false;
bool AntSumar=false;
bool AntDigito=false;
bool activo = false;
byte lastButtonState = 0;   
byte buttonState = 0;

long ultimo_reg = 0;
byte seg = 0;

byte pantalla_activa;
byte Digito=0;
byte Valores[4]={};





void setup() 
{
  pinMode(buttonPin, INPUT);  
  pinMode(buttonPin_2, INPUT); 
  pinMode(buttonPin_3, INPUT);
  pinMode(Salida, OUTPUT);
  pinMode(salida_2, OUTPUT);
  digitalWrite(Salida, LOW);
  digitalWrite(salida_2, LOW); 

  Valores[3] = EEPROM.read(direccion);
  if( Valores[0] >= 255 )
  {
    Valores[0] = Valores[1] = Valores[2] = Valores[3] = 0;
  }
  else
  {
    Valores[2] = EEPROM.read(direccion++);
    Valores[1] = EEPROM.read(direccion++);
    Valores[0] = EEPROM.read(direccion++);
    direccion=0;
    T_High=Valores[0]*10 + Valores[1]*100 + Valores[2]*1000 + Valores[3]*10000;
  }


  lcd.begin(16, 2); 
  pantalla_monitoreo();
}


void loop() 
{
  if (activo == true)
  {
    digitalWrite(salida_2, HIGH);
    if (millis() > Milis_Ciclo)            // Final ciclo, cargamos nuevo tiempo y activamos salida
    {
      Milis_Ciclo = millis() + T_Ciclo;    
      Milis_High = millis() + T_High;
      digitalWrite(Salida, HIGH);                 
    }
    if (millis() > Milis_High)            // Final salida high, desactivar salida
    {
      digitalWrite(Salida, LOW);
      ;
    }
  }
  else
  {
    digitalWrite(salida_2, LOW);
    digitalWrite(Salida, LOW);
  }

  // Boton para activar salidas
  buttonState = digitalRead(buttonPin_3);
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      if (activo == false) 
      {
        activo = true;
        if (pantalla_activa == 0)
        {
          lcd.clear(); 
          pantalla_monitoreo(); 
        }
      }
      else
      {
        activo = false;
        if (pantalla_activa == 0)
        {
          lcd.clear(); 
          pantalla_monitoreo(); 
        }
      }
    }
  }
  delay(250);
  lastButtonState = buttonState;

  //Boton aumentar valores digito
  BtnSumar=digitalRead(buttonPin);
  if (BtnSumar && AntSumar != BtnSumar)    // Boton añadir valor cambia a activo
  {
    switch (pantalla_activa)
    {
    case 0:
      lcd.clear();
      pantalla_pregunta();
      break;

    case 1:
      Digito=0;
      Valores[0] = Valores[1] = Valores[2] = Valores[3] = 0;
      lcd.clear();
      PintaTiempo();
      break;

    case 2:
      Valores[Digito]++;
      if (Valores[Digito]>9) {
        Valores[Digito]=0;
      }
      lcd.clear();
      PintaTiempo();
      break;
    }
  }
  AntSumar=BtnSumar;

  //Boton fijar valor
  BtnDigito=digitalRead(buttonPin_2);
  if (BtnDigito && AntDigito != BtnDigito)    // Boton digito cambia a activo
  {
    switch (pantalla_activa)
    {
    case 0:
      pantalla_monitoreo();
      break;

    case 1:
      lcd.clear();
      pantalla_monitoreo();
      break;

    case 2:
      Digito++; 
      if (Digito>3)  
      {  
        T_High=Valores[0]*10 + Valores[1]*100 + Valores[2]*1000 + Valores[3]*10000;// Almacenamos el nuevo tiempo
        EEPROM.write(direccion, Valores[3]);
        EEPROM.write(direccion++, Valores[2]);
        EEPROM.write(direccion++, Valores[1]);
        EEPROM.write(direccion++, Valores[0]);
        direccion = 0;
        if (T_High<T_Ciclo) 
        {
          lcd.clear();
          pantalla_monitoreo();
        }    // Del cuarto digito al primero
        else if (T_High>T_Ciclo)          // Si el tiempo high es mayor que el del ciclo pasamos el tiempo a 0
        {
          Digito=0;
          Valores[0] = Valores[1] = Valores[2] = Valores[3] = 0;
          T_High=0;
          PintaTiempo();
        }
        break;
      }

    }
  }
}


// Función pantalla principal
void pantalla_monitoreo()
{
  pantalla_activa = 0;
  lcd.setCursor(0,0); 
  lcd.write("SALIDAS ");
  if (activo == true)
  {
    lcd.setCursor(8,0); 
    lcd.write("ON");
  } 
  else
  {
    lcd.setCursor(8,0); 
    lcd.write("OFF");
  } 
  lcd.setCursor(0,1); 
  lcd.write("T ON S1 = ");
  lcd.setCursor(10,1); 
  lcd.print(Valores[3]);
  lcd.setCursor(11,1);
  lcd.print(Valores[2]);
  lcd.setCursor(12,1);
  lcd.print("'");
  lcd.setCursor(13,1);
  lcd.print(Valores[1]);
  lcd.setCursor(14,1);
  lcd.print(Valores[0]);
}

// Función pantalla pregunta
void pantalla_pregunta()
{ 
  pantalla_activa = 1;
  lcd.setCursor(0,0); 
  lcd.write("CAMBIAR TIEMPO?");
  lcd.setCursor(0,1); 
  lcd.write("1=VOLVER   2=0K");
}


// Función tiempo seleccionado      
void PintaTiempo()

{
  pantalla_activa = 2;
  lcd.setCursor(0,0);
  lcd.print(Valores[3]);
  lcd.setCursor(1,0);
  lcd.print(Valores[2]);
  lcd.setCursor(2,0);
  lcd.print(":");
  lcd.setCursor(3,0);
  lcd.print(Valores[1]);
  lcd.setCursor(4,0);
  lcd.print(Valores[0]);
}

Échale un ojo a ver que te parece:

void loop() 
{
   Anteriormilis=millis();   // Almacenamos millis() para controlar el desborde
   
   ...
  
  if (activo == true)
  {
    digitalWrite(salida_2, HIGH);
    if (millis() > Milis_Ciclo)            // Final ciclo, cargamos nuevo tiempo y activamos salida
    {
      Milis_Ciclo = millis() + T_Ciclo;    
      Milis_High = millis() + T_High;
      digitalWrite(Salida, HIGH);                 
    }
    if (millis() > Milis_High)            // Final salida high, desactivar salida
    {
      digitalWrite(Salida, LOW);
    }
   
    ...

    if (millis()<Anteriormilis)    // Si es millis es menor que el que hemos almacenado, es qaue ha desbordado y empezado de 0
    {
      Milis_Ciclo = (Anteriormilis - Milis_Ciclo) + millis(); // Nuevo valor para Milis_Ciclo : el timepo que le falta de consumir + millis()
      ...                                                     // Igual con todas las variables que sirvan para lo mismo 
    }
  }

PD: Se me olvidaba comentarte que la epron del arduino tiene una vida limitada, http://arduino.cc/es/Reference/EEPROMWrite

Esto si que es rapidez. Ya he integrado en el código la solución que me ofreces, de aquí a 50 días sabré si lo he hecho bien :astonished: (es broma, no lo dejare 50 días en marcha, si hay problemas ya me vendrán a buscar). No acabo de entender a que otras variables te refieres al decir lo de “Igual con todas las variables que sirvan para lo mismo”. Quieres decir con todas las variables que utilizan millis() para su calculo supongo. Lo de los ciclos de lectura y escritura ya lo sabía pero no creo que lleguen al límite con la utilidad que le darán, parece que sera una cosa bastante constante y repetitiva incluso en el factor tiempo. Lo que no acabo de tener claro es si seria buena practica el uso de un contador para que no sobrescriba siempre los datos en la misma dirección, o considerando (como entiendo que es) que el limite de ciclos es global y no por direcciones, dejarlo hacer siempre en el mismo sitio. ¿Cómo lo ves tú? Gracias otra vez por tu tiempo.

Sobre el tema de los tiempos, si me refería a que si utilizas varias variables para control de tiempo en sitios distintos, que las actualices todas juntas. Sobre la vida útil de la eepron, supongo que sera grabaciones ne la misma dirección, pues la vida útil estara limitada por problemas físicos. Sobre contar o no el numero de accesos ... si pero solo si es en números romanos para conseguir una buena precisión :P, o eso o ser lo suficientemente sensato como para ver si se trata de una acceso diario o un acceso cada 10 décimas de segundo, y tener controlado el tema para que de tiempo al menos a cobrar el trabajo antes de que casque , jajajaj

Ok. Gracias por las aclaraciones. Espero haber sido un alumno aplicado ,haber seguido bien las indicaciones y al final que todo esto se vea en el funcionamiento del artilugio. Y si no, como dije el otro día, ya me vendrán a buscar. Gracias por todo a todos y en especial a ti Marcial. Nos vemos

Por mi parte no se merecen.

Suerte con el artilugio.