Programa con modo Demo y salir de él

Hola
Tengo un proyecto en desarrollo.
El montaje consiste en:
• Un Arduino pro mini
• Un mando a distancia de RF
• Una pantalla LCD 16x2 I2C
• Un modulo de relé que activa una cerradura eléctrica
La pantalla LCD va mostrando mensajes en modo cíclico y cada ciclo dura unos minutos, hasta que se pulsa el mando para abrir la puerta y muestra en mensaje “PUERTA ABIERTA” durante unos segundos.
La patilla común (se pone a 5V cuando se pulsa cualquier botón del mando) del RF está conectada al pin 2 para que active una interrupción al ponerse en HIGH.
El código básicamente es:

#define PIN_MANDO 2
#define PIN_RELE 10
#define screenDir 0x27 // Dirección I2C de la pantalla
LiquidCrystal_I2C lcd(screenDir, 16, 2); // Inicia la pantalla
volatile boolean Puerta_Abierta = false; // Variable para indicar el estado del relé

void setup()
{
lcd.init(); //Inicializa el LCD
lcd.setBacklight(HIGH); //Enciende la pantalla
attachInterrupt(digitalPinToInterrupt(PIN_MANDO), interrupccion,RISING); // debe cambiar el estado de la variable del estado de la puerta
}

void loop()
{
//Aquí van las llamadas a las funciones en el modo demo
if (Puerta_Abierta) abrePuerta(); // Si hay que abrir la puerta, salta a la función de activación del relé
void LCDanimateText(“EJEMPLO”,”TEXTO”);
}

//Ejemplo de una función de texto animado
void LCDanimateText(String texto1, String texto2)
{
	boolean sale = random(2); //Elige aleatoriamente si sale el texto por la derecha de la pantalla o se queda parado en el centro
	String textop;
	lcd.clear();
	textop = "                " + texto1;
	lcd.home();
	lcd.print(textop);
	textop = "                " + texto2;
	lcd.setCursor(0, 1);
	lcd.print(textop);
	for (byte n = 0; n < 24 + sale * 12; n++)
	{
		lcd.scrollDisplayRight();
		if (n == 23) waitBreak(1500);
		if (Puerta_Abierta) return;
		waitBreak(100);
	}
	if (sale) lcd.clear();
}
//Función de espera sin dealys
void waitBreak(unsigned long milisegundos)
{
	// Retarda los milisegundos que se envían hasta que se pulse un botón
	// Mantiene un tiempo inactivo el programa sin interferir en las interrupciones
	unsigned long tempIni = millis(); // Lee el tiempo a la entrada de la función
	while ((millis() - tempIni) < milisegundos)
	{
		if (Puerta_Abierta)
			break; // sale si hay un botón pulsado
	}
}

//La función que debe tratar la interrupción
void interrupcion()
{
//Filosofía de esta función de interrupción: Entra, haz lo mínimo posible y sal pitando
Puerta_Abierta=true;
}

//Función para abrir la puerta. No me importa usar delay en esta función.
void abrePuerta()
{
Lcd.clear();
LCDanimateText(“PUERTA ABIERTA”, “”);
digitalWrite(PIN_RELE,LOW);
delay (1000); //Mantiene activo el relé durante 1 segundo
digitalWrite (PIN_RELE,HIGH); // desactiva el relé
Puerta_Abierta = false; // cambia el flag de puerta abierta a falso
delay (5000); // Mantiene el texto durante 5 segundos
}

Esto funciona perfectamente. Pero, como se puede ver en el ejemplo, hay que ir comprobando periódicamente el estado de la variable Puerta_Abierta para salir del modo demo y no se me ocurre otra forma mejor de hacerlo, seguro que es una tontería, pero no se me ocurre otra manera y no he encontrado (o he sabido buscar) algo que me pueda ayudar. Hay que tener en cuenta que en el loop hay muchas más funciones de animaciones de texto e iconos que aún no tengo terminadas.
No me parece una forma “elegante” ya que no es lo que se pretende con las interrupciones, y ya es sólo por optimizar y aprender más. Si alguien me puede dar una pista o echar un cable.

El proyecto tengo en mente mejorarlo con un mp3 para dar efectos sonoros o voces.

Muchas gracias y espero que también el código le pueda servir a alguien.

El problema es que usas delay() y con delay no hay modo de hacer que las cosas sean fluidas.

Misma recomendación que a todo el mundo. Usa millis() y lee en Documenación la máquina de estados.
Tienes que cambiar el concepto de como programar usando millis() y máquina de estados.

En Documentación hay un 1er hilo con un indice de temas. Ahi figuran claramente los dos.
Leelos y luego lo debatimos.

Surbyte, como siempre, un placer leer tus respuestas.

He vuelto a leer la maquina de estados para ver que es lo que tendría “mal” y los millis los entendí a la primera y como verás en la función waitBreak está aplicada esa fiosofía para no usar un delay.

Por alguna razón o malentendimiento veía en mi código una máquina de estados. Pero después de leer, el codigo lo reescribo por el siguiente:

#define PIN_MANDO 2
#define PIN_RELE 10
#define screenDir 0x27 // Dirección I2C de la pantalla
LiquidCrystal_I2C lcd(screenDir, 16, 2); // Inicia la pantalla
volatile boolean Puerta_Abierta = false; // Variable para indicar el estado del relé
byte demoNumber=0;

void setup()
{
lcd.init(); //Inicializa el LCD
lcd.setBacklight(HIGH); //Enciende la pantalla
attachInterrupt(digitalPinToInterrupt(PIN_MANDO), interrupccion,RISING); // debe cambiar el estado de la variable del estado de la puerta
}

void loop()
{
if (Puerta_Abierta) abrePuerta(); // Si hay que abrir la puerta, salta a la función de activación del relé
demoMode(demoNumber);
demoNumber = demoNumber<10 ? demoNumber+1:0; //hay 10 modos de Demo
}

void demoMode(byte demoType){
switch case(demotype)
{
Case 0:
LCDanimateText(“EJEMPLO”,”TEXTO”);
Break;
Case 1:
//Otra animación
Break;
// y así hasta el demo 9
}
}

//Ejemplo de una función de texto animado
void LCDanimateText(String texto1, String texto2)
{
	boolean sale = random(2); //Elige aleatoriamente si sale el texto por la derecha de la pantalla o se queda parado en el centro
	String textop;
	lcd.clear();
	textop = "                " + texto1;
	lcd.home();
	lcd.print(textop);
	textop = "                " + texto2;
	lcd.setCursor(0, 1);
	lcd.print(textop);
	for (byte n = 0; n < 24 + sale * 12; n++)
	{
		lcd.scrollDisplayRight();
		if (n == 23) waitBreak(1500,true);
		if (Puerta_Abierta) return;
		waitBreak(100,true);
	}
	if (sale) lcd.clear();
}
//Función de espera sin dealys
void waitBreak(unsigned long milisegundos, boolean state)
{
	// Retarda los milisegundos que se envían hasta que se pulse un botón
	// Mantiene un tiempo inactivo el programa sin interferir en las interrupciones
	unsigned long tempIni = millis(); // Lee el tiempo a la entrada de la función
	while ((millis() - tempIni) < milisegundos)
	{
		if (state) // si false solo espera y no responde a la pulsación
		{
			if (Puerta_Abierta) break; // sale si hay un botón pulsado
}
	}
}

//La función que debe tratar la interrupción
void interrupcion()
{
//Filosofía de esta función de interrupción: Entra, haz lo mínimo posible y sal pitando
Puerta_Abierta=true;
}

//Función para abrir la puerta.
void abrePuerta()
{
Lcd.clear();
LCDanimateText(“PUERTA ABIERTA”, “”);
digitalWrite(PIN_RELE,LOW);
waitBreak (1000,fasle); //Mantiene activo el relé durante 1 segundo
digitalWrite (PIN_RELE,HIGH); // desactiva el relé
waitBreak (5000,false); // Mantiene el texto durante 5 segundos
Puerta_Abierta = false; // cambia el flag de puerta abierta a falso
}

Esto creo que se parece más a una máquina de estados y sí, creo que hay que comprobar siempre el estado de las variables, ya sea en un loop (como dice el tutorial) o en cada una de las funciones.

Respecto al delay en la función de abrir la puerta, como puse en el comentario, no afecta que se use, ya que durante la apretura no es necesario, ni debe, leer el estado del boton aunque cambie la variable “Puerta_Abierta” si se ha pulsado de nuevo el boton durante los 6 segundos que debe haber para que se pueda abrir la puerta. Puedo deshabilitar y volver a habilitar las interrupciones mientra se abre la puerta, pero como sale siempre de la función con “false” no se ejecutará a no ser que sea pulsado de nuevo el botón.
Tambien he eliminado el delay modificando la función waitBreak con otra variable que indique si tiene que salir inmediatamente con el boton leido o solo esperar para mostrar el texto o aguantar 1 segundo el relé activo, por lo que no veo la diferencia entre usar ahí delay o millis() (y no digo que no la haya).
Solo soy un aficionado a la programación y seguro que se puede hacer más elegante y seguro que con menos código.
Muchas gracias surbyte, como siempre, aprendiendo de los mejores.