temporizador sin delay + bluetooth

Hola.
Soy un novato en el mundo arduino, llevo solo 2 meses jugando a ratos libres.

Estoy en un proyecto para controlar el vaciado de una depuradora y una luz por bluetooth. Voy a seguir añadiendo funciones, como riego controlado por electrovalvulas, mas luces, etc...

Tengo ya la aplicación para android diseñada y el software para el arduino con delay, y funciona perfectamente, salvo que cuando se ejecuta el delay no puedo hacer nada mas hasta que finalice, y por ese motivo necesito poner temporizadores como encender el rele de la depuradora durante 5min y que se apague, al igual que la luz.

Aquí os pongo el código que me funciona perfectamente:

void setup()
{
  pinMode(8, OUTPUT); // depuradora pin 8
  pinMode(7, OUTPUT); // luz depuradora pin 7
  Serial.begin(9600);
}

void loop()
{
  while (Serial.available())
  {
    char dato = Serial.read();
    digitalWrite(7, LOW);
    digitalWrite(8, LOW);
    switch (dato)
    {
      case 'A':  // enciendo depuradora
        {
          digitalWrite(8, HIGH);
          Serial.println("Rele1 encendido");
          break;
        }
      case 'B':   // apago depuradora
        {
          digitalWrite(8, LOW);
          Serial.println("Rele1 apagado");
          break;
        }
      case 'E':   // enciendo depuradora durante 5 min
        {
          digitalWrite(8, HIGH);
          Serial.println("Rele1 encendido");
          delay(300000);
          digitalWrite(8, LOW);
          break;
        }
      case 'C':  // enciendo luz depuradora
        {
          digitalWrite(7, HIGH);
          Serial.println("Rele2 encendido");
          break;
        }
      case 'D':   // apago luz depuradora
        {
          digitalWrite(7, LOW);
          Serial.println("Rele2 apagado");
          break;
        }
      case 'F':  // enciendo luz depuradora durante 15 min
        {
          digitalWrite(7, HIGH);
          Serial.println("Rele2 encendido");
          delay(900000);
          digitalWrite(7, LOW);
          break;
        }
      case 'G':  // enciendo luz depuradora durante 30 min
        {
          digitalWrite(7, HIGH);
          Serial.println("Rele2 encendido");
          delay(1800000);
          digitalWrite(7, LOW);
          break;
        }
    }
  }
}

Esta es la aplicación que he diseñado:

He intentado usar este código que encontré, añadiendo lo que yo pienso que necesito, pero no consigo que funcione:

#define ON HIGH                 // Cambiar para logica positiva/negativa 
#define OF LOW                  // Cambiar para logica positiva/negativa 
byte Pinentrada=10;
byte Pinled=7;
unsigned long T_Control=0;      // Para controlar el tiempo consumido 
unsigned long T_Apagar=2000;    // Tiempo de funcionamiento
bool EstadoPin=false;           
bool AnteriorEstadoPin=false;
void setup()
{
  pinMode(Pinled, OUTPUT);      
  pinMode(Pinentrada, INPUT);
  digitalWrite (Pinled, OF);
}
void loop()
{ 
  EstadoPin=digitalRead(Pinentrada);    // Leer pin
  if (AnteriorEstadoPin != EstadoPin)   // Estado ha variado?
  {
    T_Control=millis()+T_Apagar;        // Valor de millis para apagar
    digitalWrite (Pinled, ON);          // Encender          
  }
  AnteriorEstadoPin=EstadoPin;
  if (millis()>T_Control && T_Control>0)// Finalozo el tiempo y lo estamos controlando?
  {
    digitalWrite (Pinled, OF);          // Apagar
    T_Control=0;                        // Dejamos de controlar
  }
}

Podéis ayudarme a sustituir los delay, no termino de entender bien la función millis.

Muchas gracias.

Hola,

La función millis() devuelve un float con los milisegundos que han pasado desde que se inicia la sesión actual (la que está funcionando en el momento) hasta que se ejecuta esa línea de código (millis()). Haciendo uso de ella se pueden medir los intervalos de tiempo que se desee, para lo que hay que utilizar dos o tres variables que almacenen los tiempos, algo así como "encendido", "actual", "transcurrido", cuyos valores se tienen que ir actualizando según se necesite, normalmente una vez cada lazo principal (loop()).

Hay en el foro una especie de tutorial "Blink without delay" que probablemente te aclarará bastantes cosas.

El proyecto que estás poniendo en marcha no es fácil (tampoco es muy difícil), pero para un principiante tiene su miga (y más con las dudas que planteas). Te aconsejo que lo aparques hasta que domines las cuestiones básicas.

El proyecto: ¿es para una aplicación digamos "real"?. Si es así hay bastantes cosas que deberías tener en cuenta (aparte de la programación del arduino). Por cierto, no se ve la imagen que adjuntas.

Saludos

Moderador: No repitas lo que se lee arriba

Gracias por su respuesta. Voy a ver ese post e intentare entender la función milles.

El proyecto es real, ya lo tengo en uso, incluso alimento al arduino con una placa solar de 6v y una bateria pequeña de moto. Para este verano voy a poner un invernadero y quería automatizar todo lo posible, ya que yo con poner unos botones para encender la depuradora, luces o riegos, no tengo problema, ahí si me funciona el código de millis que puse antes, mi problema es como hacerlo desde bluetooth, que lo estoy haciendo para que mi mujer pueda controlar fácilmente todo desde el movil sin necesidad de ir hasta la depuradora o el invernadero. El tema de encender las luces por intervalos de tiempo es para cuando suelto a los perros, cuando se apaga la luz vuelven corriendo a casa.

pongo otra vez el link de la imagen de la aplicación, se puede ver pulsando con el botón derecho encima del icono y abrirlo en otra pestaña.

Apk

Hola,

El día que explicaron lo de Bluetooth no fui a clase ...

¿Podrías explicarme brevemente qué le llega al arduino (variable que cambia, stream de datos, o lo que sea) cuando tú pulsas en el teléfono lo que sea (botón, ...).

Saludos.

(Muy bueno lo de los perros ...)

En el movil lo que envía en cada botón es una letra, no se si se llama variable, A, B, C... Use un código que explicaba como encender un led por bluetooth + diseñar la app, yo solo amplíe con mas opciones como lo de poner un tiempo y el apartado de luces, simplemente cambie el led por un relé.

Estoy practicando lo de los millis, pero todo los ejemplos que explican son de como hacer que un led se encienda y apague cada x segundos, y no consigo hacer que solo encienda y apague una vez y luego se quede esperando a una nueva orden.

Adjunto imágenes de la app y su código para que veas el funcionamiento:

josuvas:
Estoy practicando lo de los millis, pero todo los ejemplos que explican son de como hacer que un led se encienda y apague cada x segundos, y no consigo hacer que solo encienda y apague una vez y luego se quede esperando a una nueva orden.

Hola,

No suelo meterme mucho a la hora de ayudar directamente con el código, pero en tu caso voy a hacer una excepción por las siguientes razones:

  1. Porque parece, a juzgar por los correos, que eres un tío bastante normal.
  2. Porque el código que adjuntas está claro, es justo lo que hay que escribir (aunque no sea lo suficientemente sofisticado para tu necesidad).
  3. Porque a la hora de pedir ayuda entiendes que tienes que poner algo de tu parte.
  4. Porque mi área de trabajo es bastante cercana a lo que estás haciendo (en general, no que sea específicamente para eso) y, si alguien más quiere aprender, lo que te explicaré es bastante útil.

Voy a hacer un borrador de un sistema que A) responda a una demanda (i.e.: la "A" desde el móvil) que ponga en marcha algo durante un tiempo "T"; B) Quede a la espera, "mientras tanto", de otra orden desde el móvil; C) Si ésta le llega -digamos "B"- pone en marcha otro proceso que dura "X"; ambos procesos siguen en marcha "simultáneamente"; D) Acepta, además, la orden "X" que para el proceso puesto en marcha en "A" y la orden "Y" que hace lo propio con el de "B"; todo ello mientras que uno o ambos procesos siguen en marcha. Evidentemente se trata de que las temporizaciones no detengan el arduino.

Yo empleo una técnica que se llama autómata de estados y cuya programación es, digamos, prolija. Haremos algo más simple para que lo puedas complicar (varias temporizaciones, ordenes) lo que necesites. Hacer el borrador me llevará un rato (quizá no lo pueda postear hasta mañana por la mañana -horario de España), asi que, de momento, vete mirando cómo se trocea el código en "funciones" para que la programación de lo anterior no se convierta en una cascada de "ifs" (código spaghetti). Lo suyo sería crear una función para cada uno de los "case" de lo que tienes, -para aprender-, pero con que hagas una (la "A", por ejemplo, vale).

Saludos

@josuvas por favor usa etiquetas para las imagens adjuntas tal como yo hice con tu primer link.
Te envié privado que esperé hubieras leído pero veo que no.
Las imágenes en lo posible deben verse para facilitar la lectura.

vffgaston:
La función millis() devuelve un float con los milisegundos que han pasado desde que se inicia la sesión actual (la que está funcionando en el momento) hasta que se ejecuta esa línea de código (millis()).

La función millis() devuelve un unsigned long.
Ojo con eso!!

vffgaston:
... vete mirando cómo se trocea el código en "funciones" para que la programación de lo anterior no se convierta en una cascada de "ifs" (código spaghetti). Lo suyo sería crear una función para cada uno de los "case" de lo que tienes, -para aprender-, pero con que hagas una (la "A", por ejemplo, vale).

Por cierto, vete mirando también lo de los arrays de variables que también nos va a hacer falta.
Saludos.

(Fíjate en lo de la variable que devuelve millis(); Surbyte -yo tengo una memoria horrorosa).

Gracias por la ayuda. Seguiré estudiando hoy y mañana todo lo que pueda y seguire los consejos que me dais. Entre semana no puedo dedicarle mucho tiempo por el trabajo y el niño pequeño.

Pido disculpas al moderador por adjuntar las imágenes así. Ley su mensaje y adjunte las imágenes, pero veo que no lo hice bien, no estoy acostumbrado a postear en foros. No se repetirá.

josuvas:
Gracias por la ayuda. Seguiré estudiando hoy y mañana todo lo que pueda y seguire los consejos que me dais. Entre semana no puedo dedicarle mucho tiempo por el trabajo y el niño pequeño.

Vale, mejor si no hay prisa.

Ya estoy con ello; llevo cuatro páginas (para publicarlo habrá que trocearlo en varios post). No preocuparse, es casi todo explicación ...

Quiero tenerlo acabado (un par de páginas más) antes de publicarlo. Casi seguro que esta tarde lo hago.

Saludos.

I)

Programacioón sin delay(). DISCLAIMER: (El ejemplo se desarrollará principalmente con “pseudo código”. Como eventualmente el pseudo código puede ser casi una instrucción en C, se piden disculpas por anticipado si la sintaxis no es completamente correcta. Puede, además, que algunas variables debiesen ser constantes y/o se pudiese usar una variable de menor tamaño –i.e.: “int” en lugar de “long”; si hay algún purista a mano no vendría mal que lo mencione/corrija).

Este artículo trata sobre la técnica elemental de programación de un arduino para conseguir que ejecute varias tareas “al tiempo”. Se desarrolla un “pseudo código” para un ejemplo con las siguientes especificaciones: El sistema A) responde a una demanda (i.e.: la "A" desde el PC/móvil) que ponga en marcha algo durante un tiempo "T"; B) Queda a la espera, "mientras tanto", de otras ordenes desde el serial monitor/móvil; C) Si le llega la orden de arrancar otro proceso -digamos "B"- lo pone en marcha: este dura "X"; ambos procesos siguen en marcha "simultáneamente"; D) También acepta la orden "X" que para el proceso puesto en marcha en "A" y la orden "Y" que hace lo propio con el de "B"; todo ello mientras que uno o ambos procesos siguen en marcha. Finalmente, todo se compatibiliza con la intermitencia del LED incorporado en el arduino para monitorizar su funcionamiento (que no esté colgado). Evidentemente se trata de que las temporizaciones de salidas y LED no detengan el arduino.
(El lector tiene que tener un conocimiento medio del entorno arduino y -del “subset”- del lenguaje de programación “C++” que se usa habitualmente para programarlo).

Lo primero, la filosofía:
El microcontrolador del arduino –cualquier modelo- es tan rápido que salvo que se le pare con ordenes tipo “delay()”, es capaz de responder instantáneamente (con la percepción humana como baremo) a las ordenes y/o sucesos externos (e internos –temporizaciones-, como es el caso de este ejemplo). Si se sigue el esquema que propone el IDE de arduino, el programa (o “sketch”) se divide en tres partes: 1) La de inicialización, 2) el “setup” y 3) el “loop”; el programa propiamente dicho se almacena en el “loop”, que se ejecuta continuamente con una periodicidad –para un programa de tamaño medio- de decenas de microsegundos o menos. (de ahí, lo breve del loop, la cuasi inmediatez de la respuesta.
El programa (en este ejemplo) tiene que responder a los siguientes eventos:

  1. Requerimiento del usuario vía teclado de PC (o aplicación de móvil; ambas entran por “serial”) de arranque/parada de procesos externos.
  2. Finalización de las temporizaciones de los aparatos puestos en marcha por el usuario y de los semiperíodos del LED del arduino para parar los procesos y encender/apagar el LED.
    La técnica consiste en utilizar las funciones de tiempo (habitualmente “millis()”) junto con algunas variables asociadas a cada uno de los procesos (arranque/parada de aparatos; LED) para conseguir la correspondiente temporización. Evidentemente hay que actualizar las variables y compararlas con los tiempos requeridos para controlar los procesos. De esta forma, al ejecutar continuamente el loop, el uC escanea continuamente los eventuales comandos del usuario, modificando, si procede, el comportamiento del sistema.
    (Para sistemas muy complejos se recomienda implementar lo anterior mediante la técnica de “automata de estados finito” –tambien conocido como “autómata de estados”, “autómata de estados finitos”, “finite estate automata”, FEA ó, incluso “finite estate automaton” (no es coña)-. En este ejemplo se usará una especie de “subconjunto” de esta técnica; se irá explicando sobre la marcha cuándo y qué forma parte de esto.

II)

Implementación (I):

LED:
int loopPeriod = 500; // Led flashing: 500 ms ON; 500 mS OFF
bool loopPerFlag = 1; // Aux. flag LCD (variable)
long lastLoopPer = 0; // Aux. LCD (variable)

Aparato “A”:
int APeriod = 5000; // El aparato “A” tiene que funcionar durante 5 segundos y parar
bool APerFlag = 1; // Aux. flag (variable)
long lastAPer = 0; // Aux. buffer (variable)

Aparato “B”:
int BPeriod = 50000; // El aparato “B” tiene que funcionar durante 50 segundos y parar
bool BPerFlag = 1; // Aux. flag (variable)
long lastBPer = 0; // Aux. buffer (variable)

(NOTA: lo suyo sería utilizar la técnica de “programación orientada a objetos” –OOP- en cuyo caso estas variables se asignarían a cada uno de los objetos –“aparatoA”, “aparatoB”, …- al ser “construidos”. Vamos a dejarlo ahí y utilizaremos la técnica convencional –“procedural”- para este ejemplo)

Se necesitarán, además, las siguientes variables:
int evento = 0; // Para saber lo que ha pedido el usuario y/o si ha terminado alguna temporización.
int transicion; // Lo que hay que hacer ya.
Int eventosTransiciones [0, 1, 2, 3, 4, 5, 2, 4]; // transición que corresponde a cada evento.
(NOTA1: los términos –y uso que se hace aquí- “evento” y “transición” corresponden con los homólogos en un FEA).
(NOTA2: según cada uno programe, estas variables se podrían “retornar” –return- por las correspondientes funciones en lugar de ser definidas como “globales”; ya que no se trata de un master sobre programación, sino de que se aprenda a utilizar el “timer”, tampoco nos vamos a poner estupendos con ello).
Todas estas variables se definen en la inicialización. En esta también irán los nombres de los pines –i.e.: int ledPin = 13;-.

III)

Implementación (II):
El esquema del sketch completo es:
1 Inicialización
2 Lo anterior

Setup:
Lo habitual: baudios, modos de los pines
La inicialización de las variables propias de la aplicación se hizo al definirlas.

3 Loop:
{
/* En general se debe trocear el código en funciones aunque no sea más que para facilitar el mantenimiento posterior del programa. En este caso tendremos las funciones siguientes:
*/
deteccion(); // Ver si el usuario pide algo y/o ha terminado una temporización
ejecucion(); // Hacer lo que toque
}
(Se insiste en que, aunque a veces no lo parezca, esto es “pseudo código”)
Bien, vamos con las funciones:

////////////////////////////////////////////////////////////////////////////////
void deteccion()
/* Esta función examina el buffer serie y comprueba los timers del sistema para detectar si hay que realizar alguna tarea. Actualiza la variable “evento” de acuerdo con la siguiente tabla:
0	No hay evento que atender
1	el usuario ha solicitado que arranque el aparato A
2	Ídem “B”
3	el usuario ha solicitado que pare el aparato A
4	Ídem “B”
5	Fin de período del LED
6	Fin temporización aparato “A”
7	Fin temporización aparato “B”
*/

{
	if (Serial.available() > 0)
	// El usuario ha tecleado algo
	{
	    char dato = Serial.read();
    switch (dato)
    {
           case 'A':  // encender “A”
   	{evento = 1; break;}	
	
          case 'B':  // encender “B”
   	{evento = 2; break;}		

	… resto de eventos hasta 4

	return;					// No comprobar resto de eventos
} // Fin eventos serial.

	// Comprobar si hay que apagar encender el LED      
      	if ((millis() - lastLoopPer) > loopPeriod)
      	{evento = 5; return;}

// Comprobar temporización aparato “A”
If (APerFlag == 1);			// Comprobar solo si está en marcha
If ((millis() - lastAPer) > APeriod)
      	{evento = 6; return;}

// Comprobar temporización aparato “B”
If (BPerFlag == 1);			// Comprobar solo si está en marcha
If ((millis() - lastBPer) > BPeriod)
      	{evento = 7; return;}
	
} 	// Fin deteccion()


////////////////////////////////////////////////////////////////////////////////
void ejecucion()
/* Esta función ejecuta lo que haya que hacer en función del valor de evento

0	No hay evento que atender
1	el usuario ha solicitado que arranque el aparato A
2	Ídem “B”
3	el usuario ha solicitado que pare el aparato A
4	Ídem “B”
5	Fin de período del LED
6	Fin temporización aparato “A”
7	Fin temporización aparato “B”
*/


{

    transicion = eventosTransiciones[evento];
    /* 
     Esta instrucción es una de las más importantes del programa (junto con el contenido del array “eventosTransiciones[]”). En función del evento que haya sucedido, se decide que es lo que hay que hacer. Nótese que hay diferentes eventos que disparan la misma transición.
    */
    switch (transicion)
    {
      case '1':  // Arrancar “A”
        {
          ArrancarA();
          break;
        }

      case '2':  // Arrancar “B”
        {
          ArrancarB();
          break;
        }

            case '3':  // Parar A
        {
          PararA();
          break;
        }

            case '4':  // Parar B
        {
          PararB();
          break;
        }

      case '5':  // Apagar/encender LED
        {
          LED();
          break;
        }

} 	// Fin deteccion()


//////////////////////////////////////////////////////////////////////////////////
//		TRANSICIONES
//////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////
Void LED()
  {
      // Update signalization (flashing LED)
      
      if ((millis() - lastLoopPer) > loopPeriod)
      {
        // Time to change
        if (loopPerFlag)   // 0 to 1 or 1 to 0?
        {
          // 1 to 0
          digitalWrite(sigPin, LOW);
          loopPerFlag = LOW; // Change flag
        }
        else
        {
          // 0 to 1
          digitalWrite(sigPin, HIGH);
          loopPerFlag = HIGH; // Change flag
        }
        // Store time
        lastLoopPer = millis();
      }
  }

//////////////////////////////////////////////////////////////////////////////////
Void ArrancarA()
        {
          digitalWrite(pinA, HIGH);
          Serial.println("Rele1 encendido");
	lastAPer = millis();		// Resetear el contador
	APerFlag = 1;		// Situación de A
        }

//////////////////////////////////////////////////////////////////////////////////
Void ArrancarB()
        {
          digitalWrite(pinB, HIGH);
          Serial.println("Rele2 encendido");
	lastBPer = millis();		// Resetear el contador
	BPerFlag = 1;		// Situación de B
        }


//////////////////////////////////////////////////////////////////////////////////
Void PararA()
        {
          digitalWrite(pinA, LOW);
          Serial.println("Rele1 apagado");
	APerFlag = 0;		// Situación de A
        }

//////////////////////////////////////////////////////////////////////////////////
Void PararB()
        {
          digitalWrite(pinB, LOW);
          Serial.println("Rele2 apagado");
	BPerFlag = 0;		// Situación de B
        }

Bien, si lo implementas no vendría mal que pases el código definitivo.

No lo intentes todo al tiempo: empieza solo con el "arranqueA", después, poco a poco, el resto.

Una pregunta: ¿con el negocio ese del Bluetooth: los "serial.print" por dónde salen, por el teléfono?

Saludos

Muchísimas gracias por su ayuda.

Disculpadme no contestar antes, pero se me fastidio el imac y me lo acaban de entregar. Lo primero que he echo es mirar este post. Jejjj

voy a estudiar todo lo que me has comentado y el código, antes de probarlo, para ver si lo entiendo todo.

Gracias de nuevo y te comentare que tal me ha funcionado y el código final que usare.

Los serial.print no salen por el teléfono, los uso para comprobar las ordenes desde monitor serie antes de pasarlo al arduino nano que tengo funcionando con esos 2 relés, ya que solo tengo un modulo bluetooth y es un jaleo estar sacándolo de la caja donde lo tengo. Las practicas las hago con un arduino uno.

Estoy estudiando el código que me has pasado para entenderlo bien, me han surgido muchas dudas, pero las voy resolviendo poco a poco buscando información.
Una pregunta, cuando intento crear la variable:

Int eventosTransiciones [0, 1, 2, 3, 4, 5, 2, 4];

me da error al copilarlo sobre los corchetes []. Viendo un tutorial que hice de un chaleco para ciclistas, que se encienden 10 luces, utilizamos una variable parecido, pero se escribe de otra forma, lo escribí así y no me da error al copilarlo:

int eventosTransiciones[] = {0, 1, 2, 3, 4, 5, 2, 4};

Esta bien así, o no hace la misma función.