Problema con cronómetro digital láser con Arduino

Buenas a todos.

Hace poco diseñé un cronómetro digital para utilizarlo en los circuitos de Slot. Básicamente consiste en un laser situado a un extremo de la pista y un receptor situado al extremo contrario. El coche, al interrumpir el láser, genera una interrupción. De esta forma controlo las vueltas y los tiempos y envío dicha información mediante puerto serie a una tabla Excel.

El problema es el siguiente: algunos coches (no sé si será porque interrumpen el láser varias veces al pasar) cuentan el tiempo al cruzar el láser. Y luego, un tiempo más tarde, sin que el coche atraviese el láser se marca otra vuelta de repente. Para evitar el problema de los coches con recovecos (que hacen que el láser pase y se corte varias veces mientras están pasando) puse un tiempo mínimo de 2 segundos en los que no se cuenta la vuelta. Con lo cual no sé qué puede ser el problema. Os dejo el código para que le echéis un vistazo

Código:
#define PINLASER 4
#define PINRECEP 2
#define PINPULSA 6
#define PINALIM  8
#define PINGND   7

//Variables globales
bool primera = true;
unsigned long tAntiguo, tNuevo, tVuelta;
int trazado = 250; //Recorrido total del trazado en cm (actualizar por cada circuito)
byte vuelta = 1;
float velMedia;
int estadoBoton, estadoBotonAnterior; //Para implementar el antirrebote SW

//Reseteo del contador cuando se presiona el botón
void reset(){
    Serial.print("DATA,DATE,"); //Imprime la fecha
    Serial.print("TIME,");      //Imprime la hora
    Serial.print(vuelta);       //Imprime el nº de vuelta
    Serial.print(",");
    vuelta++;
    Serial.print("VUELTA NULA"); //Indica vuelta nula
    Serial.print(",");
    Serial.println("N/A");

    primera = true;
}




//ISR para pin del receptor #1
void calculaTiempo(){
  
  if(primera){
    tAntiguo = millis();
    primera = false;
  }
  
  else{

    if(millis() - tAntiguo > 2000){

      tNuevo = millis();
    
      //Calculo de tiempo de vuelta
      tVuelta = (tNuevo - tAntiguo)/1000;
      
      //Adaptacion a MM:SS
      int minutos = tVuelta/60;
      int segundos = tVuelta - (tVuelta/60) * 60;
      int milesimas = (tNuevo - tAntiguo) - (tVuelta)*1000;
      String cadenaTiempo;
      
      //Impresión en archivo excel
      if(segundos < 10)
        cadenaTiempo = String(String(minutos) + String(":" + String(String("0" + String(segundos))))+String("." + String(milesimas)));
      else
        cadenaTiempo = String(String(minutos) + String(":" + String(segundos))+String("." + String(milesimas)));
      
      Serial.print("DATA,DATE,"); //Imprime la fecha
      Serial.print("TIME,");      //Imprime la hora
      Serial.print(vuelta);       //Imprime el nº de vuelta
      Serial.print(",");
      vuelta++;
      Serial.print(cadenaTiempo); //Imprime el tiempo de vuelta
      Serial.print(",");
      Serial.print("=36*G1/");
      Serial.println((tNuevo-tAntiguo));
      
      tAntiguo = tNuevo;
  }
  }
  
}

void setup(){
  //Abre la comunicación serial y etiqueta las columnas del archivo excel
  Serial.begin(9600);
  Serial.println("CLEARDATA"); //Para borrar la información que había antes
  Serial.println("LABEL,Fecha,Hora,Vuelta,Tiempo,Vel. Media (km/h)"); //Imprime las etiquetas
  
  //Pin del láser
  pinMode(PINLASER, OUTPUT);
  digitalWrite(PINLASER,HIGH);

  //Pin para alimentar el botón
  pinMode(PINALIM, OUTPUT);
  digitalWrite(PINALIM, HIGH);
  pinMode(PINGND, OUTPUT);
  digitalWrite(PINGND, LOW);

  //Pin para leer del botón
  pinMode(PINPULSA, INPUT);
  estadoBotonAnterior = digitalRead(PINPULSA);
  
  //Activación de las interrupciones
  attachInterrupt((PINRECEP -2),calculaTiempo,FALLING);
  
}

void loop(){
  
  estadoBoton = digitalRead(PINPULSA);
  if(estadoBoton != estadoBotonAnterior){
    if(estadoBoton){
      delay(100);
      reset();
    }
    else delay(100);
  }
  estadoBotonAnterior = estadoBoton;
  
}
...
 
Actualización:

Veamos, ya solucioné el problema. Era un fallo muy tonto, para quien le sirva mi código. Resulta que la función millis() no funciona adecuadamente en el interior de una ISR, con lo cual tuve que sacar el código fuera de la función ISR y utilizar ésta únicamente para activar una booleana que te permite entrar en la nueva función, todo esto asumiendo una cierta pérdida de precisión.

Os pongo el código por si alguno lo queréis (hay que compartir en comunidad!!)

Código:
#define PINLASER 4
#define PINRECEP 2
#define PINPULSA 6
#define PINALIM  8
#define PINGND   7

//Variables globales
bool primera = true, interrupcion = false;
unsigned long tAntiguo, tNuevo, tVuelta;
int trazado = 250; //Recorrido total del trazado en cm (actualizar por cada circuito)
byte vuelta = 1;
float velMedia;
int estadoBoton, estadoBotonAnterior; //Para implementar el antirrebote SW

//Reseteo del contador cuando se presiona el botón
void reset(){
    Serial.print("DATA,DATE,"); //Imprime la fecha
    Serial.print("TIME,");      //Imprime la hora
    Serial.print(vuelta);       //Imprime el nº de vuelta
    Serial.print(",");
    vuelta++;
    Serial.print("VUELTA NULA"); //Indica vuelta nula
    Serial.print(",");
    Serial.println("N/A");

    primera = true;
}



void interrupcionLaser(){
  interrupcion = true;
}


//ISR para pin del receptor #1
void calculaTiempo(){
  
  if(primera){
    tAntiguo = millis();
    primera = false;
  }
  
  else{

    if(millis() - tAntiguo > 1000){

      tNuevo = millis();
    
      //Calculo de tiempo de vuelta
      tVuelta = (tNuevo - tAntiguo)/1000;
      
      //Adaptacion a MM:SS
      int minutos = tVuelta/60;
      int segundos = tVuelta - (tVuelta/60) * 60;
      int milesimas = (tNuevo - tAntiguo) - (tVuelta)*1000;
      String cadenaTiempo;
      
      //Impresión en archivo excel
      if(segundos < 10)
        cadenaTiempo = String(String(minutos) + String(":" + String(String("0" + String(segundos))))+String("." + String(milesimas)));
      else
        cadenaTiempo = String(String(minutos) + String(":" + String(segundos))+String("." + String(milesimas)));
      
      Serial.print("DATA,DATE,"); //Imprime la fecha
      Serial.print("TIME,");      //Imprime la hora
      Serial.print(vuelta);       //Imprime el nº de vuelta
      Serial.print(",");
      vuelta++;
      Serial.print(cadenaTiempo); //Imprime el tiempo de vuelta
      Serial.print(",");
      Serial.print("=36*G1/");
      Serial.println((tNuevo-tAntiguo));
      
      tAntiguo = tNuevo;
  }
  }
  
}

void setup(){
  //Abre la comunicación serial y etiqueta las columnas del archivo excel
  Serial.begin(9600);
  Serial.println("CLEARDATA"); //Para borrar la información que había antes
  Serial.println("LABEL,Fecha,Hora,Vuelta,Tiempo,Vel. Media (km/h)"); //Imprime las etiquetas
  
  //Pin del láser
  pinMode(PINLASER, OUTPUT);
  digitalWrite(PINLASER,HIGH);

  //Pin para alimentar el botón
  pinMode(PINALIM, OUTPUT);
  digitalWrite(PINALIM, HIGH);
  pinMode(PINGND, OUTPUT);
  digitalWrite(PINGND, LOW);

  //Pin para leer del botón
  pinMode(PINPULSA, INPUT);
  estadoBotonAnterior = digitalRead(PINPULSA);
  
  //Activación de las interrupciones
  attachInterrupt((PINRECEP -2),interrupcionLaser,FALLING);
  
}

void loop(){
  
  estadoBoton = digitalRead(PINPULSA);
  if(estadoBoton != estadoBotonAnterior){
    if(estadoBoton){
      delay(100);
      reset();
    }
    else delay(100);
  }
  estadoBotonAnterior = estadoBoton;

  if(interrupcion){
    calculaTiempo();
    interrupcion = false;
  }
  
}

AHORA NUEVA PREGUNTA:

Algunos coches marcan más de una vez en la vuelta sin pasar por el láser. No tengo muy claro por qué, pero ocurre. Eso sí, les pasa sólo a algunos, a otros NUNCA. He comprobado a ver si era por el color del coche (a lo mejor algún reflejo afectaba), pero no tiene nada que ver.

Tengo sospechas de que pueda ser la vibración al pasar por algunas partes de la pista, porque a veces se oye un ruido y marca una vuelta. Pero he probado a sacudir un poco la pista y la meta en sí y en ningún caso saltaba el flanco.

¿Qué se os ocurre que puede ser? A ver si me podéis ayudar porque ando ya loco con esto... Os pongo fotos del montaje por si os sirven. Gracias!
 

Adjuntos

  • IMG_20160119_183659[1].jpg
    IMG_20160119_183659[1].jpg
    77.2 KB · Visitas: 20
  • IMG_20160119_183709[1].jpg
    IMG_20160119_183709[1].jpg
    53.4 KB · Visitas: 22
  • IMG_20160119_183723[1].jpg
    IMG_20160119_183723[1].jpg
    56.8 KB · Visitas: 19
  • IMG_20160119_183755[1].jpg
    IMG_20160119_183755[1].jpg
    86.3 KB · Visitas: 18
  • IMG_20160119_183810[1].jpg
    IMG_20160119_183810[1].jpg
    72.2 KB · Visitas: 15
Podría ser ruido eléctrico al pasar un coche cerca del receptor láser. ¿El circuito del receptor láser y la pista de los coches están alimentados por fuentes separadas?. ¿El circuito receptor láser está apantallado para no ser perturbado por ruido eléctrico de los motores y contactos de las escobillas de los coches?.
 
La pista de slot que hicimos con mi padre utiliza reed switches para detectar el paso de los autos, aprovechando el imán que poseen debajo. Tengo incluso el circuito si te interesa, funciona con 555 y 4026 que entregan las vueltas en display de 7 segmentos.

Off Topic: muy linda la pista ;)
 
Podría ser ruido eléctrico al pasar un coche cerca del receptor láser. ¿El circuito del receptor láser y la pista de los coches están alimentados por fuentes separadas?

Así es. El circuito se alimenta por medio de un USB conectado a PC mientras que el circuito se alimenta por medio de su transformador comercial.

¿El circuito receptor láser está apantallado para no ser perturbado por ruido eléctrico de los motores y contactos de las escobillas de los coches?.

A qué te refieres exactamente? No uso ninguna protección, los coches pasan tal cual.



La pista de slot que hicimos con mi padre utiliza reed switches para detectar el paso de los autos, aprovechando el imán que poseen debajo. Tengo incluso el circuito si te interesa, funciona con 555 y 4026 que entregan las vueltas en display de 7 segmentos.

Off Topic: muy linda la pista ;)

Muchas gracias! El problema es que algunos modelos son antiguos (clásicos) y carecen de imán. Fue algo que me planteé hacer en su momento, pero vi mas universal el método óptico. Ahora me arrepiento XD
 
No sé cual es el nombre correcto, pero los antiguos mouse de bolita traían un componente que leía el giro de la bolita por medio de una rejilla que pasaba entre un emisor-receptor óptico (algo así cómo un emisor infrarrojo acoplado a un fototransistor que "veía" las interrupciones de la rejilla circular y sumaba o restaba a las coordenadas X e Y de la posición del mouse).

Se podría colocar uno de estos en la ranura por la que son guiados los vehículos (el slot, propiamente dicho).

Yo también considero que si pudieses hacer funcionar el láser sería mucho más adecuado y "contemporáneo", pero es otra solución alternativa.

Saludos y sigue contando tus avances.

Ahora que lo recuerdo los reed switches nos producian un "efecto rebote" en los 4026. Corregí ese problema colocando un diodo 1N4148 a la entrada de los mismos.

No sé si funcionara en tu caso, pero no pierdes nada con probar. ;)

Respecto a lo que te sugiere Troglodita: él se refiere a que si estas utilizando un cable común, sin cobertura coaxil, el lector podría estar captando ruido electromagnético presente en el aire (ondas de radio, señales de celular, etc). Podría ser algo a observar. Prueba con cable apantallado mono, del que se utiliza en audio.
 
Última edición:
No sé cual es el nombre correcto, pero los antiguos mouse de bolita traían un componente que leía el giro de la bolita por medio de una rejilla que pasaba entre un emisor-receptor óptico (algo así cómo un emisor infrarrojo acoplado a un fototransistor que "veía" las interrupciones de la rejilla circular y sumaba o restaba a las coordenadas X e Y de la posición del mouse).

Te refieres a un encoder?

Ahora que lo recuerdo los reed switches nos producian un "efecto rebote" en los 4026. Corregí ese problema colocando un diodo 1N4148 a la entrada de los mismos.

No sé si funcionara en tu caso, pero no pierdes nada con probar. ;)

Respecto a lo que te sugiere Troglodita: él se refiere a que si estas utilizando un cable común, sin cobertura coaxil, el lector podría estar captando ruido electromagnético presente en el aire (ondas de radio, señales de celular, etc). Podría ser algo a observar. Prueba con cable apantallado mono, del que se utiliza en audio.

El caso es que se activan estas interrupciones pasado más de un segundo de recorrido (para evitar efecto rebote puse que no se consideraran interrupciones durante el primer segundo tras cruzar la meta). Y, sinceramente me parece muy raro que pueda tratarse de un efecto rebote taaan largo. Ojalá tuviera un osciloscopio... :(

En cuanto al cable, empleo un cable común de electrónica, sin cobertura coaxial. Pero si el problema son las interferencias como dices, tiene que tratarse de interferencias que se reciben de algunos coches en concreto. Algunos no producen este efecto nunca, por muchas vueltas que le de al circuito.
 
Atrás
Arriba