Bucle con millis y Pulsador

Hola. Describo el programa: Al accionar un pulsador abrimos una puerta hasta un punto que paramos. Ahí quiero que durante un tiempo se espere por el pulsador y si se ha pulsado salir del bucle de espera y si no se pulsó pasado el tiempo también se salga del bucle. Eso es posible?. El programa está resumido. Gracias.

unsigned long start = millis() + 20000UL; //fijamos el tiempo
const int fcaPin = 3;
const int paPin = 5;
int fca;
int pa = HIGH;
int MAPin = 7;      //Motor abrir                
int MCPin = 8;		//Motor cerrar

void setup() {
	fca = digitalRead(fcaPin);
	pa = digitalRead(paPin);
}

void loop() {

	while (pa == HIGH) {	// **Leemos el pulsador mediante un bucle
		pa = digitalRead(paPin);
	}

	while (fca == LOW) {	// Abrimos 
		abre();
		fca = digitalRead(fcaPin);
	}

	para();			//al final de carrera paramos
	
//*** bucle de espera 
//*** Esperamos por el pulsador 
//*** si pasado el tiempo "start" no se ha pulsado... 
	while (millis() < start){
		while (pa == HIGH) { 
			pa = digitalRead(paPin);	// **Leemos el pulsador
		} 
	}
	//*** ...salir bucle de espera
	
	cierra();
	digitalWrite(MAPin, HIGH);
	digitalWrite(MCPin, HIGH);
	delay(10000);
}

// Funciones
void para() {	// Funcion PARAR
	digitalWrite(MAPin, LOW);     // Ponemos los pines 6 y 7 en valor bajo
	digitalWrite(MCPin, LOW);     // Vale poniendolos en valor alto
}

void abre() {	// Funcion ABRIR
	digitalWrite(MAPin, HIGH);     // Motor abre la puerta
	digitalWrite(MCPin, LOW);
}

void cierra() {	// Funcion CERRAR
	digitalWrite(MAPin, LOW);     // Motor cierra la puerta
	digitalWrite(MCPin, HIGH);
}

Hola, Primero sigue las nosmas del foro correctamente. Es fácil, edita el post, selecciona todo el código y pulsa </> en la barra del editor, guardas y listo.
En cuanto a tu consulta no veo exactamente de que bucle quieres salir ya que has puesto
la linea fuera de cualquier bucle.

//* ...salir bucle de espera

dode realmente tenga que ir esa linea sustituyela por simplemente

break;

Saludos.

Hola. Lo siento gonpezzi: No domino este portal todavía. No he sabido editar el post.
En cuanto al programa, aunque le pongo break; sigue sin funcionar. Seguiré buscando y haciendo combinaciones a ver si es por tener dos while.
Saludos`

unsigned long start = millis() + 20000UL; //fijamos el tiempo
const int fcaPin = 3;
const int paPin = 5;
int fca;
int pa = HIGH;
int MAPin = 7;      //Motor abrir                
int MCPin = 8;		//Motor cerrar

void setup() {
	fca = digitalRead(fcaPin);
	pa = digitalRead(paPin);
}

void loop() {

	while (pa == HIGH) {	// **Leemos el pulsador mediante un bucle
		pa = digitalRead(paPin);
	}

	while (fca == LOW) {	// Abrimos 
		abre();
		fca = digitalRead(fcaPin);
	}

	para();			//al final de carrera paramos
	
//*** bucle de espera 
//*** Esperamos por el pulsador 
//*** si pasado el tiempo "start" no se ha pulsado... 
	while (millis() < start){
		while (pa == HIGH) { 
			pa = digitalRead(paPin);	// **Leemos el pulsador
		} 
	}
	//*** ...salir bucle de espera
	
	cierra();
	digitalWrite(MAPin, HIGH);
	digitalWrite(MCPin, HIGH);
	delay(10000);
}

// Funciones
void para() {	// Funcion PARAR
	digitalWrite(MAPin, LOW);     // Ponemos los pines 6 y 7 en valor bajo
	digitalWrite(MCPin, LOW);     // Vale poniendolos en valor alto
}

void abre() {	// Funcion ABRIR
	digitalWrite(MAPin, HIGH);     // Motor abre la puerta
	digitalWrite(MCPin, LOW);
}

void cierra() {	// Funcion CERRAR
	digitalWrite(MAPin, LOW);     // Motor cierra la puerta
	digitalWrite(MCPin, HIGH);
}

`

Moderador:
Por favor, lee las Normas del foro y edita tu código usando etiquetas de código.
Ve a edición, luego selecciona todo el error que has publicado, lo cortas y click en </>


Todo eso es posible pero no como lo estas haciendo y menos con un delay(10000) al final que detiene el código.
Yo usaría una máquina de estados y iría reconociendo cada estado de funcionamiento de la puerta y asi construir lo que quieres. Incluyendo tu delay() usando millis() y con un código que no se pierde ningún evento.
Si te interesa primero ve a Documentación => Indice de temas tutoriales => máquinas de estados y también refuerza el uso de millis().

Hazle caso a @Surbyte y lee un poco mas sobre el uso de millis, perdona por contestarte sin haber profundizado mucho en tu codigo inicial pero adema tenias otras cosillas mal, una de ellas era que en en setup no definias la funcion de los pines, por el codigo que tenias he supuesto que al pulsarlos llevan al pin a gnd. Le he heho unos arreglillos a tu codigo, estan comentados en el y ahora deberia funcionar.
No coloco las funciones porque me parecen correctas

unsigned long inicio_cuenta;
const int fcaPin = 3;
const int paPin = 5;
int MAPin = 7; //Motor abrir
int MCPin = 8; //Motor cerrar
int fca;
int pa = HIGH;


void setup() {
  pinMode (paPin, INPUT_PULLUP);
  pinMode (fcaPin, INPUT_PULLUP);
  pinMode (MAPin, OUTPUT);
  pinMode (MCPin, OUTPUT);
  fca = digitalRead(fcaPin);
  pa = digitalRead(paPin);
}

void loop() {

  while (pa == HIGH) {  // **Leemos el pulsador mediante un bucle indefinidamente
    pa = digitalRead(paPin);
  }
  // Se pulso, hemos salido del bucle
  abre();//se ha pulsado el boton ¡Hay que abrir ya!
  while (fca == HIGH) {  // Esperamos se pulse el boton fin de carreramientras seguimos abriendo
    fca = digitalRead(fcaPin);
  }
  // Se pulso, hemos salido del bucle
  para();//al final de carrera paramos
  
  //*** bucle de espera
  //*** Esperamos por el pulsador
  //*** si pasado el tiempo y no se ha pulsado...
  inicio_cuenta = millis();//Marca para controlar el tiempo que transcurre
  while (millis() - inicio_cuenta <= 20000) {//se sale del bucle al pasar el tiempo definido
    pa = digitalRead(paPin);
    if (pa = LOW) {
      break; // si durante el tiempo de espera se pulsa paPin se rompe el bucle y continua el codigo.
    }
 }
    cierra();// empezamos a cerrar
    // las dos lineas siguieentes las comento porque paran el motor immediatamente
    //  digitalWrite(MAPin, HIGH);
    //  digitalWrite(MCPin, HIGH);
    delay(10000);// haces el fin cierra por tiempo yo añadiria otro pulsador fin de carrera y usaria un while como anteriormente.
    para();// Hemos cerrado la puerta
  }

Saludos.

Hola. En primer lugar gracias @gonpezzi y @Surbyte. Seguiré aprendiendo de las indicaciones hechas... aunque ya la neurona que quizás me queda no de mucho de sí.
El programa esta resumido para que, dentro de lo posible fuera entendible y ejecutable. Lo he rodado son simuladores pero en el bucle de los millis no funcionaba.
Por mi este tema lo doy por resuelto. Muchas gracias de todas formas.
Sigo aprendiendo.
Saludos
JoseV

Hola jjosev, he modificado tu programa con una máquina de estados, que creo haberte pedido que leyeras.
En una máquina de estados, siempre hay un solo estado posible, y describe el funcionamiento de tu puerta paso a paso, permite tambien de un estado ir a cualquiera de los otros, teniendo mucho cuidado de no crear ciclos que se repitan sin sentido que en realidad ocurrirían por nuestro error al describir que ocurre.
En tu caso yo he armado la máquina leyendo lo que tu y @gonpezzi han escrito.

unsigned long inicio_cuenta;
const int fcaPin  = 3;
const int paPin   = 5;
const int MAPin   = 7; // Motor abrir
const int MCPin   = 8; // Motor cerrar
int fca, pa;
byte estado = 0; // variable utilizada para la máquina de estados.

void setup() {
  pinMode (paPin, INPUT_PULLUP);
  pinMode (fcaPin, INPUT_PULLUP);
  pinMode (MAPin, OUTPUT);
  pinMode (MCPin, OUTPUT);
  fca = digitalRead(fcaPin);
  pa = digitalRead(paPin);
}

void loop() {

  switch(estado) {
    case 0: // **Leemos el pulsador mediante un bucle indefinidamente
            pa = digitalRead(paPin);
            if (!pa) {      // Se pulso, cambio de estado
                abre();
                estado = 1;
            }
            break;
    case 1: // espero el fin de carrera
            fca = digitalRead(fcaPin);
            if (!fca) {  // Esperamos se pulse el boton fin de carrera mientras seguimos abriendo
                para();
                estado = 2;  // cambio de estado.
                inicio_cuenta = millis();//Marca para controlar el tiempo que transcurre
            }
            break;
    case 2: // bucle de espera, esperamos por el pulsador o por el tiempo.
            pa = digitalRead(paPin);
            if (millis() - inicio_cuenta > 20000 || !pa) {
                cierra();// empezamos a cerrar
                estado = 3; // si superamos 20 segundos o se presionó el pulsador cambio de estado.
                inicio_cuenta = millis();//Marca para controlar el tiempo que transcurre
            }       
            break;
    case 3: if (millis() - inicio_cuenta > 10000) {
                para();
                estado = 0;  // Vuelvo al comienzo??
            }
    }
  }

// Funciones
void para() {  // Funcion PARAR
  digitalWrite(MAPin, LOW);     // Ponemos los pines 6 y 7 en valor bajo
  digitalWrite(MCPin, LOW);     // Vale poniendolos en valor alto
}

void abre() { // Funcion ABRIR
  digitalWrite(MAPin, HIGH);     // Motor abre la puerta
  digitalWrite(MCPin, LOW);
}

void cierra() { // Funcion CERRAR
  digitalWrite(MAPin, LOW);     // Motor cierra la puerta
  digitalWrite(MCPin, HIGH);
}

Si lo miras con tranquilidad piensa que acá nada detiene el flujo del programa. Uso millis() no uso delay(), no detengo al microcontrolador en ningún momento y siempre estoy observando los pulsadores involucrados.
Prueba si funciona y si no, ya me dirás.

Hola. Muchas gracias Surbyte. Me parece una idea genial. Para mis pocos conocimientos me sobrepasa. Un vistazo ahora por encima me parece que va a estar muy bien. Lo probaré mañana que se me ha liado la "agenda?" (ironía). Tu verás que agenda tiene un jubileta.
Saludos
JoseV

Mira, no hay mucho que entender pero por eso te pedí que fueras a documentación y leyeras una buena explicación de qué es una máquina de estados.
Como dije, TODO se desmenuza en situaciones unica. Cada una constituye un estado, y de este sales si solo si se cumple alguna acción.
Entonces he transformado algo que tenia DEMORAS debido a tu delay() a algo que fluye muy veloz aunque hace lo mismo, todo el tiempo esta vigilando lo que debe vigilar.
estado = 0 que en un switch case es case 0:
leo el pin
si es low por eso pregunto if(!pa) esto es lo mismo que if (pa == LOW)
ahora bien, si es low que hago, abro la puerta y paso al siguiente estado = 1
En el estado 2 me quedo de por vida hasta que el fin de carrera dice, Hey!!! estoy en LOW, entonces detengo con para() y paso al siguiente estado = 2.
y acá inicializo la variable que luego compararé con millis() porque de ese modo lo hago una vez.
Voy al caso 2
leo el pin pa y pregunto si se ha cumplido el tiempo de 20seg
como lo hago= ? con un OR que son estos ||
las dos condiciones o supero 20 seg o pa == LOW hacen que cierre() y pase a 3
Como ya no usaré mas inicio_cuenta para el caso anterior, lo vuelvo a usar para el nuevo caso que me sirve para eliminar el delay(10000)
voy a caso 3 y cuando supero 10 seg detengo con para() y repito todo desde 0 esperando el pulsador pa, etc, etc.

Hola. Surbyte, muy interesante y práctico lo de Máquinas de estado. Como verás he hecho algunas modificaciones para adaptarlo al uso real, sobre todo en los casos 2 y 3.
Los delays del final eran para controlar yo con el simulador.
Para que se entienda se trata de el control de una puerta de garaje ya que la electrónica de control no funcionaba bien. Solo aprovecho la decodificación del telemando.
Ahora voy a atreverme a implementar también el control con la célula fotoeléctrica, que deberá parar y abrir en el caso de que esté bajando la puerta y se interrumpa dicha célula.
Pongo mi código. Saludos.
JoseV

unsigned long inicio_cuenta;
const int fcaPin  = 3;
const int paPin   = 5;
const int fccPin  = 6;
const int MAPin   = 7; // Motor abrir
const int MCPin   = 8; // Motor cerrar
int fca, pa, fcc;
byte estado = 0; // variable utilizada para la máquina de estados.

void setup() {
  pinMode (paPin, INPUT_PULLUP);
  pinMode (fcaPin, INPUT_PULLUP);
  pinMode (MAPin, OUTPUT);
  pinMode (fccPin, INPUT_PULLUP);
  pinMode (MCPin, OUTPUT);

  fca = digitalRead(fcaPin);
  pa = digitalRead(paPin);
  fcc  = digitalRead(fccPin);
}

void loop() {

  switch(estado) {
    case 0: // **Leemos el pulsador mediante un bucle indefinidamente
            pa = digitalRead(paPin);
            if (!pa) {      // Se pulso, cambio de estado
                abre();
                estado = 1;
            }
            break;
    case 1: // espero el fin de carrera
            fca = digitalRead(fcaPin);
            //fcc = digitalRead(fccPin);
            if (!fca) {  // Esperamos se pulse el boton fin de carrera mientras seguimos abriendo
                para();
                estado = 2;  // cambio de estado.
                inicio_cuenta = millis();//Marca para controlar el tiempo que transcurre
            }
            break;
    case 2: // bucle de espera, esperamos por el pulsador o por el tiempo.
                      fca = digitalRead(fcaPin);
                      pa = digitalRead(paPin);
                      fcc  = digitalRead(fccPin);
            if (millis() - inicio_cuenta > 20000 || !pa) {
                cierra();// empezamos a cerrar
                estado = 3; // si superamos 20 segundos o se presionó el pulsador cambio de estado.
                inicio_cuenta = millis();//Marca para controlar el tiempo que transcurre
            }       
            break;
    case 3: fcc = digitalRead(fccPin);
            if (!fcc) {  // Esperamos se pulse el boton fin de carrera mientras seguimos abriendo
                para();
                        estado = 0;  // Vuelvo al comienzo??
                        }
                break;
           
    }
  }

void para() {  // Funcion PARAR
  digitalWrite(MAPin, LOW);     // Ponemos los pines 6 y 7 en valor bajo
  digitalWrite(MCPin, LOW);     // Vale poniendolos en valor alto
}

void abre() { // Funcion ABRIR
  digitalWrite(MAPin, HIGH);     // Motor abre la puerta
  digitalWrite(MCPin, LOW);
}

void cierra() { // Funcion CERRAR
  digitalWrite(MAPin, LOW);     // Motor cierra la puerta
  digitalWrite(MCPin, HIGH);
}

Mañana lo probaré en realidad.

Bueno, primero debo felicitarte porque antes que sentirte abrumado te has dado cuenta que es incluso MAS FACIL de ver y entender. Permite manejar las cosas desde otra óptica y cuando realmente lo comprendes y usas, verás que es la mejor manera de programar.
Esto es en microprocesadores lo que muchos dicen de multriprocesamiento (aunque sabemos que no existe, al menos en estos micros). Da la sensación que hace muchas cosas pero sigue siendo secuencial pero como no pierde tiempo realmente sorprende.
Y mira de tu post#7 donde parecías abatido a este otro @jjosev que esta con gran entusiasmo, la verdad me da mucho gusto.
Adelante!!