[SOLUCIONADO] Encendido alterno de leds con opción de pausa

Buenos días Arduineros, soy nuevo por aquí y novato trasteando arduino. Les comento un problema que tengo con el código ante un pequeño proyecto que estoy realizando. He estado buscando por el foro e internet pero no encuentro la solución a mi problema, espero que me puedan ayudar. :slight_smile:

El código en si debe activar dos pistones neumáticos durante un tiempo de forma alterna, estos pistones en principio los estoy simulando encendiendo y apagando dos leds. Lo que deben hacer es lo siguiente.

Cuando comienza el código debe encender el primer led durante 0.5 segundos y permancer apagado 4 segundos, momento en el cual el segundo se encide 0.5 segundos y permanece apagado 4 segundos. Adicionalmente le he añadido un contador para que esto lo realice durate X repeticiones, esto me lo muestra en pantalla 20x4. El código lo tendría que realizar sin delay. En principio esto lo he conseguido con el siguiente código:

#include <Wire.h>
#include <LiquidCrystal_I2C_AvrI2C.h>

LiquidCrystal_I2C_AvrI2C lcd(0x27, 20, 4);



const int piston1 = 12;
const int piston2 = 11;



int estado_piston1 = LOW;
int estado_piston2 = LOW;


long previousMillis1 = 0;
long previousMillis2 = 0;
long contador = 20;




long intervalOn = 7500;
long intervalOff = 500;
long intervalOn2 = 500;
long intervalOff2 = 7500;
long desfase = 4000;





void setup()
{
  Serial.begin(9600);
  lcd.begin();
  lcd.backlight();
  
  pinMode(piston1, OUTPUT);
  pinMode(piston2, OUTPUT);
}

void loop()
{
  
  while (contador > 0 ) {

        

    lcd.setCursor(0, 1); lcd.print("Num CAMBIOS");
    lcd.setCursor(12, 1); lcd.print(contador);
    unsigned long currentMillis1 = millis();
    unsigned long currentMillis2 = currentMillis1 + desfase;
    
    if ((estado_piston1 == LOW) && (currentMillis1 - previousMillis1 >= intervalOn))
    {
      estado_piston1 = HIGH;
      previousMillis1 = currentMillis1;
      digitalWrite(piston1, estado_piston1);

    }
    else if ((estado_piston1 == HIGH) && (currentMillis1 - previousMillis1 >= intervalOff))
    {
      estado_piston1 = LOW;
      previousMillis1 = currentMillis1;
      digitalWrite(piston1, estado_piston1);
      contador--;

    }


    if ((estado_piston2 == HIGH) && (currentMillis2 - previousMillis2 >= intervalOn2))
    {
      estado_piston2 = LOW;
      previousMillis2 = currentMillis2;
      digitalWrite(piston2, estado_piston2);

    }
    else if ((estado_piston2 == LOW) && (currentMillis2 - previousMillis2 >= intervalOff2))
    {
      estado_piston2 = HIGH;
      previousMillis2 = currentMillis2;
      digitalWrite(piston2, estado_piston2);
      contador-- ;

    }
  }
}

En principio esto lo realiza perfectamente, el problema surge cuando quiero añadir un boton de parada y otro para continuar por donde iba. Lo he hecho de la siguiente forma:

#include <Wire.h>
#include <LiquidCrystal_I2C_AvrI2C.h>

LiquidCrystal_I2C_AvrI2C lcd(0x27, 20, 4);



const int piston1 = 12;
const int piston2 = 11;


int boton1 = 3;
int boton4 = 10;

int estado_piston1 = LOW;
int estado_piston2 = LOW;


long previousMillis1 = 0;
long previousMillis2 = 0;
long contador = 20;




long intervalOn = 7500;
long intervalOff = 500;
long intervalOn2 = 500;
long intervalOff2 = 7500;
long desfase = 4000;

int estado = 1;



void setup()
{
  Serial.begin(9600);
  lcd.begin();
  lcd.backlight();  
  pinMode(boton1, INPUT);  
  pinMode(boton4, INPUT);
  pinMode(piston1, OUTPUT);
  pinMode(piston2, OUTPUT);
}

void loop()
{

  if (digitalRead(boton1) == HIGH) {
    estado = 1;    
  }
  while (contador > 0 && estado == 1) {

    if (digitalRead(boton4) == HIGH) {
      estado = 0;
    }

    lcd.setCursor(0, 1); lcd.print("Num CAMBIOS");
    lcd.setCursor(12, 1); lcd.print(contador);
    unsigned long currentMillis1 = millis();
    unsigned long currentMillis2 = currentMillis1 + desfase;
    
    if ((estado_piston1 == LOW) && (currentMillis1 - previousMillis1 >= intervalOn))
    {
      estado_piston1 = HIGH;
      previousMillis1 = currentMillis1;
      digitalWrite(piston1, estado_piston1);

    }
    else if ((estado_piston1 == HIGH) && (currentMillis1 - previousMillis1 >= intervalOff))
    {
      estado_piston1 = LOW;
      previousMillis1 = currentMillis1;
      digitalWrite(piston1, estado_piston1);
      contador--;

    }


    if ((estado_piston2 == HIGH) && (currentMillis2 - previousMillis2 >= intervalOn2))
    {
      estado_piston2 = LOW;
      previousMillis2 = currentMillis2;
      digitalWrite(piston2, estado_piston2);

    }
    else if ((estado_piston2 == LOW) && (currentMillis2 - previousMillis2 >= intervalOff2))
    {
      estado_piston2 = HIGH;
      previousMillis2 = currentMillis2;
      digitalWrite(piston2, estado_piston2);
      contador-- ;

    }
  }
}

El paro y continue lo hace correctamente pero cuando vuelve a iniciar el bucle el encendido de leds no lo hace correctamente, comienzan a encenderse los dos a la vez o con otro ritmo al anterior y nose como puedo solucionar este problema.

Gracias y un saludo!

Por lo que veo en tu código, pierdes la referencia de los previousMillis1 y 2
agrega esto a ver si asi lo hace bien

 while (contador > 0 && estado == 1) {

    if (digitalRead(btnParada) == HIGH) {
      estado = 0;                         // aca lo detienes entonces debes guardar los estados de previous
      previousMillis1 = currentMillis1;   // Agregar
      previousMillis2 = currentMillis2;   // agregar
    }

Buenos días! Gracias por contestar surbyte. He estado probando lo que me dijiste pero sigue haciendo lo mismo. Ya no tengo ni idea de que puede estar ocurriendo y no creo que deba ser dificil, es como si perdiera el desfase al volver a poner en marcha la secuencia.

Hola.
Habitualmente, cuando en un código se empiezan a encadenar series de if con condiciones complejas suele ocurrir que no hemos llegado a simplificar suficientemente nuestro problema. Surbyte suele insistir en muchos casos, y casi siempre que giramos alrededor de millis, en la utilización de una máquina de estados. Tu problema creo que sería un paradigma de máquina de estados:

-estado 0: led 1 on, led 2 off, 500 milisegundos.
-estado 1: led 1 off, led 2 off, 4000 milisegundos.
-estado 2: led 1 off, led 2 on, 500 milisegundos.
-estado 3: led 1 off, led 2 off, 4000 milisegundos.

Sólo tienes que definir esos cuatro estados y controlar el tiempo que llevas en el estado actual antes de saltar al siguiente.

Este sería un hilvanado de código. No está probado y probablemente tenga algún fallo, aunque compila; pero si eres capaz de leerlo y entenderlo no te será difícil ajustarlo a tus necesidades, y a buen seguro que a muchas necesidades futuras. Se apoya en dos conceptos que considero importantísimos en C como son las estructuras y los arrays, pues su base es esa: un array de estructuras.

const int piston1 = 12;
const int piston2 = 11;
const int boton1 = 3;
const int boton2 = 10;
struct structFase {
  byte piston1Status;
  byte piston2Status;
  unsigned long tiempoFase;
};

structFase fase[4]={
  {HIGH, LOW, 500}, 
  {LOW, LOW, 4000},
  {LOW, HIGH, 500},
  {LOW, LOW, 4000}
};


void setup()
{

  Serial.begin(9600);
  pinMode(piston1, OUTPUT);
  pinMode(piston2, OUTPUT);
}

void loop()
{
  unsigned long lastMillis;
  for (int contador=20; contador>0; contador--){
    Serial.print("Cuentas Quedan:");
    Serial.println(contador);
    for (int numFase=0; numFase<4; numFase++){
      digitalWrite(piston1, fase[numFase].piston1Status);
      digitalWrite(piston2, fase[numFase].piston2Status);
      lastMillis=millis();
      while(millis()-lastMillis < fase[numFase].tiempoFase) {
        if (digitalRead(boton1)==HIGH) {
          while (digitalRead(boton2)==LOW) {
          }
        }
      }
    }
  }
}

Muchísimas gracias noter, esto ahora funciona perfecto aunque el contador debería bajar cada vez que se enciende un led y no cada dos encendidos, para esto he hecho que el contador vaya de dos en dos, nose si hay otra forma. La verdad que es mucho más simple esto que lo que yo tenía. Espero que le pueda servir a alguien más.

Ahora me surgen algunas dudas, la primera es donde podría buscar más información acerca de este tipo de programación, para empezar a aprender ya que me parece bastante interesante.

Otra duda es si a este programa le podría poner algo así como un cronómetro para para que me diga el tiempo que ha tardado en realizar el bucle, sin contar las pausas. He de decir que he cambiado el primer bucle for por uno while.

#include <Wire.h>
#include <LiquidCrystal_I2C_AvrI2C.h>

LiquidCrystal_I2C_AvrI2C lcd(0x27, 20, 4);

const int piston1 = 12;
const int piston2 = 11;
const int boton1 = 3;
const int boton2 = 10;
const int boton3 = 9;
int numerepes = 10;
int contador = 0;



struct structFase {
  byte piston1Status;
  byte piston2Status;
  unsigned long tiempoFase;
};

structFase fase[4] = {
  {HIGH, LOW, 500},
  {LOW, LOW, 500},
  {LOW, HIGH, 500},
  {LOW, LOW, 500}
};


void setup()
{
  lcd.begin();
  lcd.backlight();
  Serial.begin(9600);
  pinMode(piston1, OUTPUT);
  pinMode(piston2, OUTPUT);
}

void loop()
{
   
  unsigned long lastMillis;
  //for (int numerepes=10; numerepes >=0; numerepes-= 2) {
    while(contador<=numerepes){
    
    lcd.setCursor(0, 0);
    lcd.print("Cuentas Quedan:");
    lcd.setCursor(15, 0);
    lcd.print(contador);
    lcd.setCursor(15, 1);
    lcd.print(numerepes);
    for (int numFase = 0; numFase < 4; numFase++) {
      digitalWrite(piston1, fase[numFase].piston1Status);
      digitalWrite(piston2, fase[numFase].piston2Status);
      lastMillis = millis();
      while (millis() - lastMillis < fase[numFase].tiempoFase) {
        if (digitalRead(boton1) == HIGH) {
          while (digitalRead(boton2) == LOW) {
          }
        }
      }
    }
    contador+=2;     
    }  
}

Un saludo.

Hola.
Como los led se encienden en las fases pares, podrías poner el incremento del contador dentro del for tal que así:

    for (int numFase = 0; numFase < 4; numFase++) {
      digitalWrite(piston1, fase[numFase].piston1Status);
      digitalWrite(piston2, fase[numFase].piston2Status);
      if (numFase==0 || numFase==2) {
        contador++;
        // Aquí instrucciones para mostrar el nuevo contador en el lcd
      }
      lastMillis = millis();
      while (millis() - lastMillis < fase[numFase].tiempoFase) {
        if (digitalRead(boton1) == HIGH) {
          while (digitalRead(boton2) == LOW) {
          }
        }
      }
    }

Ahora me surgen algunas dudas, la primera es donde podría buscar más información acerca de este tipo de programación, para empezar a aprender ya que me parece bastante interesante.

Supongo que te refieres al manejo de arrays, estructuras y demás. Pertenecen al ámbito del lenguaje C un poco avanzado. Cualquier buen manual de C/C++ te puede servir para introducirte en esos y otros muchos conceptos muy interesantes. Por ejemplo yo suelo consultar mucho este curso cuando necesito refrescar algo.

Otra duda es si a este programa le podría poner algo así como un cronómetro para para que me diga el tiempo que ha tardado en realizar el bucle, sin contar las pausas. He de decir que he cambiado el primer bucle for por uno while.

No entiendo a qué te refieres con esto. ¿Es porque necesitas que el bucle dure exactamente lo que propones?

Ahora cuenta perfecto, gracias de nuevo. He estado mirando el curso y tiene muy buena pinta, pero le tendré que echar bastante tiempo para asimilarlo todo, ya que cuando me sacan de los bucles simples...

Respecto a lo del tiempo, a lo que me refería era algo asi como implementar dentro del bucle while un cronometro marcha atrás, por ejemplo, tengo 20 repeticiones y cada una 5 segundos, lo cual hace que el bucle tarde 100 segundos, 1 minuto y 40 segundos. La idea es mostrar por pantalla cuantos minutos y segundos quedan en tiempo real, la verdad no sé si me he explicado bien del todo.

Aprovechando tengo otra duda, es sobre crear un menú con botones, realmente lo tengo hecho pero quería exponerlo para ver si se podría simplificar bastante ya que no veo la manera, pero no sé si abrir otro hilo o seguir por este.

Aunque el tema esta relacionado, la cuestión de los menúes siempre viene bien refrescarla, asi que crea un nuevo hilo en Software desde ya, y agrega SOLUCIONADO al título de este.

Perfecto lo pongo como SOLUCIONADO, aunque agradecería si alguien me hecha una mano con lo del tiempo.
Gracias.