Dudas con mantener una función sin pausar el loop

Muy buenas tardes a todos.

Estoy con un proyecto que creía bastante simple, pero ha llegado un punto en el que ya no sé qué es lo que estoy haciendo mal.

En una máquina que tenemos en fábrica queremos detectar cuando unas piezas son demasiado largas al ser alimentadas de forma automática en un proceso.

Para ello la idea era poner una chapìta abatible a cierta altura y que, cuando pasase una pieza que hiciera contacto con la chapita, durante x segundos un relé activase una electroválvula para expulsar esa pieza defectuosa.

Eso lo tengo hecho, pero me encuentro con que, si durante esos 5 segundos en los que la electroválvula está soplando, llega otra pieza larga y toca la chapita, el tiempo que el soplido está activo no vuelve a 5 segundos, si no que acaba su cuenta atrás y listo.

Me estuve informando sobre las pausas sin delay y pensaba que lo tendría listo con este sketch, pero algo hago mal:

#include <SoftwareSerial.h>

unsigned long acumulado = 0;  // Variable que incremetará su valor si las piezas cierran el contacto 
const int sensor1 = 13;       // Pin al que va conectado uno de los cables que harán de contacto
const int sensor2 = 11;       // Pin al que va conectado el otro de los cables que hará de contacto
const int rele = 7;           // Pin al que irá el relé (Mosfet) que activará el soplido
unsigned long ahora = 0;      // Variable que contendrá el valor del tiempo que hace que tocó la pieza
unsigned long gap = 0;
unsigned long diff = 0;

int valor = 0;                     // Variable que nos dará el estado del contacto entre los dos pines 

void setup() {
  pinMode(sensor1, OUTPUT);       // Declaramos que el pin del sensor 1, será solo de salida.
  pinMode(sensor2, INPUT);        // Declaramos que el pin del sensor 2, será solo de entrada.
  digitalWrite(sensor2, HIGH);    // Activamos el internal pull up del pin del sensor
  pinMode(rele, OUTPUT);
  digitalWrite(rele, HIGH);       // Conectamos un segundo el soplido poniendo el relé en HIGH para saber que todo va ok al encender
  delay(1000);
  digitalWrite(rele, LOW);        // Conectamos un segundo el soplido poniendo el relé en HIGH para saber que todo va ok al encender
  Serial.begin(9600);             // Durante la ejecución del programa en la aplicación real no será necesario el serial, para pruebas, sí  
}



// Esta es la parte del código que se va a ir ejecutando sin parar a modo de loop

void loop() {
  valor = digitalRead(sensor2);

  if (valor > 0) {      // Si no hay ningún contacto, declaramos que el valor acumulado de contactos es 0; 
    acumulado = 0;
  }

  if (valor < 1) {
    acumulado = acumulado + 1;  // Si sí que lo hay, aumentamos el valor de contacto en 1
  }

  if (acumulado > 2500) {  // Si el valor de contactos seguidos es mayor que 2500 (así evito falsos positivos)...
    ahora = millis();      // a la variable ahora le asigno el valor de millis
    gap = ahora + 5000;    // y a la variable gap el valor de ahora + 5000 ms
    Serial.println("Contacto"); // en la aplicación real no será necesario el puerto de serie
  }

  if (millis() < gap)  {   // Esta es la función que uso para soplar durante 5 segundos
    diff = gap - millis();
    digitalWrite(rele, HIGH);
    Serial.print("Soplamos (Queda por soplar ");
    Serial.print(diff);
    Serial.println(" ms)");
  }
  else
  {
    digitalWrite(rele, LOW);  // Esta parte del código necesitaré mejorarla para que no la ejecute cuando el pin ya esté en LOW,
                              // Pero es que actualmente ni así funciona :(
  }
}

Cuando hago contacto entre los dos pines que uso a modo de sensor, si no dejo de hacer contacto, lo que recibo por el puerto de serie se asemeja mucho a lo que quiero:
Una cuenta atrás que se resetea constantemente

Contacto
Soplamos (Queda por soplar 5000 ms)
Contacto
Soplamos (Queda por soplar 5000 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4989 ms)
Contacto
Soplamos (Queda por soplar 4989 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4989 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4989 ms)
Contacto
Soplamos (Queda por soplar 4989 ms)
Contacto
Soplamos (Queda por soplar 4989 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)
Contacto
Soplamos (Queda por soplar 4990 ms)

Pero si dejo de hacer contacto, la cuenta atrás va bajando y, aunque vuelva a hacer contacto cuando quedan 4000, 3000 o los ms que sean, la cuenta atrás sigue impasible hasta cero sin volver a ponerse a 5000ms:

Contacto
Soplamos (Queda por soplar 4989 ms)
Soplamos (Queda por soplar 4951 ms)
Soplamos (Queda por soplar 4912 ms)
Soplamos (Queda por soplar 4874 ms)
Soplamos (Queda por soplar 4835 ms)
Soplamos (Queda por soplar 4797 ms)
Soplamos (Queda por soplar 4758 ms)
Soplamos (Queda por soplar 4719 ms)
Soplamos (Queda por soplar 4681 ms)
Soplamos (Queda por soplar 4642 ms)
Soplamos (Queda por soplar 4605 ms)
Soplamos (Queda por soplar 4566 ms)
Soplamos (Queda por soplar 4527 ms)
Soplamos (Queda por soplar 4489 ms)
Soplamos (Queda por soplar 4450 ms)
Soplamos (Queda por soplar 4412 ms)
...

Soplamos (Queda por soplar 238 ms)
Soplamos (Queda por soplar 200 ms)
Soplamos (Queda por soplar 164 ms)
Soplamos (Queda por soplar 126 ms)
Soplamos (Queda por soplar 89 ms)
Soplamos (Queda por soplar 52 ms)
Soplamos (Queda por soplar 16 ms)

Supongo que estoy haciendo algo mal, pero no se ver bien el qué.

Cualquier consejo o ayuda será muy apreciado.

Muchas gracias!

no se si te he pillado, pero después de llevar tiempo peleado con millis() he llegado a la conclusión que cuantas menos instrucciones entre lecturas mejor

 if (acumulado > 2500) {  // Si el valor de contactos seguidos es mayor que 2500 (así evito falsos positivos)...
    ahora = millis();      // a la variable ahora le asigno el valor de millis
    gap = ahora + 5000;    // y a la variable gap el valor de ahora + 5000 ms
    Serial.println("Contacto"); // en la aplicación real no será necesario el puerto de serie

    digitalWrite(rele, HIGH);  //<----- cambia aquí el HIGH

  }

  if (millis() < gap)  {   // Esta es la función que uso para soplar durante 5 segundos
    diff = gap - millis();
 //   digitalWrite(rele, HIGH);  

//dejamos esto para ver en pantalla
    Serial.print("Soplamos (Queda por soplar ");
    Serial.print(diff);
    Serial.println(" ms)");
  }
  else
  {
    digitalWrite(rele, LOW);  // Esta parte del código necesitaré mejorarla para que no la ejecute cuando el pin ya esté en LOW,
                              // Pero es que actualmente ni así funciona :(
  }
// aunque yo anulaba todo el resto (desde if millis()) y pondría
if(millis()>gap && gap){
     digitalWrite(rele, LOW);
     gap=0;
     }

Saludos

Hola, gracias por la ayuda. Creo que me expliqué bastante mal.

El setup sería una cosa así:

Cuando detecto una pieza mala necesito mantener el soplido durante un tiempo para esperar que esa pieza mala llegue a la zona donde tengo la electroválvula (eso implica que, tomando la imagen como referencia, cuando la pieza 5 toque la trampilla, soplaremos y descartaremos también la 3 y la 4).

Si soplo durante x segundos, aunque descarte piezas que eran buenas, me aseguro que la pieza 5 será expulsada con el soplido de la electroválvula. (Lo ideal sería esperar x segundos hasta que la pieza 5 llegase a la zona de expulsión, pero no me importa descartar piezas buenas con tal de asegurarme que la 5 no sigue en la cinta).

El problema que tengo es que, si detecto la pieza 5 y soplo durante x tiempo, con el sketch que tengo, es posible que, aunque la pieza 7 toque la trampilla, al estar aún en el ciclo de soplado anterior, cuando la cuenta atrás que activó la pieza 5 llegue a cero, la pieza 7 no sea expulsada.

He probado con las modificaciones que me recomendabas ayer, pero sigue sin funcionar si cierro el contacto en medio de una cuenta atrás.

Gracias

Para comenzar cuanto demora cada pieza en moverse?
En tu esquema hay una trampilla que para mi es un interruptor de pieza larga.
Estando una pieza en contacto con dicho interruptor de pieza larga, cuanto demora una segunda pieza larga en llegar al mismo interruptor. + de 5 seg menos de 5 seg, cuanto exactamente o dicho de otro modo cual es la velocidad de la cinta transportadora de piezas?
Y a que distancia estan una pieza de otra y asi sabremos bien y estimaremos bien lo que estas pidiendo.

Buenas tardes.

La velocidad puede ser variable, pero aproximadamente pasan unas 4.000 piezas por hora.

La idea de mantener el soplido un rato es porque desde la trampilla que hace de sensor hasta donde está puesta la electroválvula las piezas tardan entre 2 y 3 segundos en llegar.

La distancia entre piezas es fija (van alojadas en orificios y asoman más o menos dependiendo de su longitud). Estamos hablando de una distancia de 10mm entre centro y centro de orificio.

A ver.. no puedes ser tan impreciso con algo que justamente requiere precision, o de lo contrario tendras que montar algo que te diga cuando hay una pieza además de la pieza larga.
Eso te daría la orden que te falta para dejar de soplar.

Comprendo que la cienta no tenga velocidad constante pero entonces aporta datos a tu Arduino para que pueda resolver el problema.
La manera a mi parecer sería

Con un sensor optico tal que corte un haz de luz IR por ejemplo. De hecho podrias usarlo para los dos casos. Uno para indicar piezas y otro para indicar piezas largas mejor que lo que estas usando, pero es una idea.

Al detectar la pieza larga tu sabes porque vas contando cuantas van entre medio y puedes accionar el soplador de modo seguro.
Se entiende?

surbyte:
A ver.. no puedes ser tan impreciso con algo que justamente requiere precision, o de lo contrario tendras que montar algo que te diga cuando hay una pieza además de la pieza larga.
Eso te daría la orden que te falta para dejar de soplar.

Comprendo que la cienta no tenga velocidad constante pero entonces aporta datos a tu Arduino para que pueda resolver el problema.
La manera a mi parecer sería

Con un sensor optico tal que corte un haz de luz IR por ejemplo. De hecho podrias usarlo para los dos casos. Uno para indicar piezas y otro para indicar piezas largas mejor que lo que estas usando, pero es una idea.

Al detectar la pieza larga tu sabes porque vas contando cuantas van entre medio y puedes accionar el soplador de modo seguro.
Se entiende?

El problema es que la velocidad puede ser cambiada en en algunos momentos. Si hay piezas "normales" o no hay piezas me da igual (la cinta va a avanzar siempre). De hecho puede haber tramos en los que no haya pieza (la máquina que las pone se ha quedado sin, fallos en fases previas, etc...)

Mi único problema son las largas. Por eso quiero soplar durante 5 segundos cuando detecte una larga al tocar la trampilla aunque eso suponga descartas piezas buenas.

Son piezas de alambre, he hecho pruebas con sensores ópticos y no eran fiables, lo único que me ha detectado al 100% ha sido sensores inductivos y por contacto. Al poder haber diferentes longitudes dentro de las NO-OK, la trampilla ha sido la mejor solución, ya que si la pieza era muy larga podía colisionar con el sensor inductivo. Para evitar falsos positivos por vibraciones he puesto la condición en el programa que no baste con un cambio de estado en el pin que detecta el contacto, si no que sea necesario un contacto seguido durante algunos ms.

De hecho, para paliar la falta de precisión en el montaje, prefiero que tan pronto hay contacto, se active la electroválvula durante un tiempo seguro (a pesar que esto signifique descartar piezas buenas que iban delante de la pieza larga).

No entiendo porqué, una vez está iniciada la cuenta atrás no soy capaz de reasignar el valor de ahora y de gap para que el soplido siga durante 5 segundos más.

Hola,

Intente entender el codigo pero me perdia asi que lo cargue en un nano para entenderlo

Agrega la linea:

Serial.println(acumulado);

En la parte que imprimes y entenderas el error.

Cuando haces contacto por primera vez (y lo mantienes) "acumulado" ya vale mas de 2500 por eso va bien, pero si desconectas "acumulado" pasara a valer 0 y ya nunca mas pasara por la zona en donse se guarda el tiempo donde se empieza a soplar, ese es el error.

Me parece que es mas sencillo:

Solo debes hacer dos cosas en el loop:

a) Si hay contacto debes guardar la hora, si no lo hay nada. De esta manera siempre tendras la hora del ultimo contacto.

b) Si estas dentro los cinco segundos posteriores al ultimo contacto (dato de hora guardado) debes soplar, si no nada.

Con respecto a los falsos positivos no entiendo bien la razon pero si dices que no te importa perder algunas piezas, en el peor de los casos solo tendras cinco segundos de perdidas en cada falso positivo.

Y una pregunta tonta, para ver si lo entiendo
¿Porque no soplas mientras está el contacto activado? es decir ¿habría algún problema de colocar el soplador enfrente (o justo donde está) del contacto? De esa forma el soplido durará lo mismo que tarda en pasar la pieza

Saludos

cas6678:
a) Si hay contacto debes guardar la hora, si no lo hay nada. De esta manera siempre tendras la hora del ultimo contacto.

Lo que dice cas6678 es la solución para saber cuando dejar de soplar.

Se puede estar a la espera de que “aparezca” una pieza. Una vez aparezca se guarda ese instante y puede esperar un tiempo a que avance antes de activar el soplador para así no expulsar algunas de las piezas que la preceden (piezas 2 y 3 de la imagen que has puesto). La pieza 5 sería expulsada por seguridad (dependería del tiempo de espera que se le ponga).

A la vez hacemos lo que propone cas6678 y guardamos en qué instante se ha detectado una pieza siempre que se detecte. Mientras se esté detectando se estará actualizando ese tiempo. Si se está soplando, se esperará a que transcurra, desde la última detección, el tiempo de espera de inicio de soplado y el tiempo mínimo que ha de estar soplando. Una vez transcurrido todo ese tiempo se deja de soplar y vuelve a la espera de detectar una nueva pieza.

Si mientras está soplando se vuelve a detectar una pieza, se vuelve a tomar como referencia ese instante hasta que deje de detectarla. Entonces ha de transcurrir todo el tiempo de espera a partir de ese último instante para que pare de soplar. Esto significa que se descartan las piezas buenas que estén entre dos piezas malas que aparezcan muy juntas.

Para implementarlo he usado dos máquinas de estado. Una para detectar la pieza “con seguridad” y evitar los falsos positivos de los que hablas. Para ello controla el “antirebote” de la señal del sensor.

La otra máquina de estados controla el soplador, según se detecte o no una pieza por la otra máquina de estados y acorde a los tiempos deseados.

Para “afinar y poner a punto” el sistema, se han de configurar tres constantes que definen tres tiempos.

TIEMPO_ANTIREBOTE

define cual es el tiempo mínimo que ha de estar la señal de entrada sin cambiar para dar por bueno que hay una pieza a descartar. Si el tiempo es muy corto puede dar muchos falso positivos, pero si es muy grande puede no detectar ninguna pieza. Yo he puesto 50 milisegundos.

TIEMPO_ESPERA_INICIO_SOPLADO

define cuánto tiempo ha de esperar entre que detecta inicialmente una pieza para empezar a soplar (para descartar la menor cantidad posible de piezas buenas). Le he puesto 2 segundos.

TIEMPO_DURACION_SOPLADO

define cual es tiempo mínimo que ha de estar soplando. Este tiempo se suma al

TIEMPO_ESPERA_INICIO_SOPLADO

y el total es el tiempo que ha de transcurrir entre que se deja de detectar la última pieza y se procede a apagar el soplador. Le he puesto 3 segundos.

Debido a que el código no me cabe junto con estos comentarios, lo adjunto.

El código está comentado. Cualquier duda, no dudes en preguntar.

foro_soplador.ino (7.09 KB)

@bosoft
Hola, gracias por el comentario. El problema es que las piezas van encajadas hasta cierta altura, por lo que la única solución es soplas desde abajo (y no puedo hacerlo justo donde está la trampilla porque las piezas no saldrían hacia arriba debido a la propia trampilla.

#IgnoranteAbsoluto
Siento la tardanza en responder (día de locos). Muchas gracias por el sketch. Está fuera de mi liga. Voy a cargarlo en una nano en casa para mirar de aprender de tu código.

Mañana iré a la empresa expresamente para probarlo in situ.

Buenas noches a todos

Siento haber tardado tanto en responder. Decir que el código de @cas6678 funciona muy bien, pero habiendo tenido que pasar de un input digital a uno analógico porque la señal que daba una pieza al contactar con la trampilla era muy débil.

Muchas grácias a todos!

Si la señal es débil, bien puedes colocar un Smith Trigger que ponga la señal como la esperas 0-5V.
Pero solo lo digo para que lo tengas en cuenta.