Problema con velocimetro

Hola
estoy preparando un velocímetro para la moto utilizando un fotointerruptor. El caso es que no consigo que marque la velocidad correctamente en el lcd, empiezan a fluctuar números incorrectos a toda velocidad. Pego el código a continuación, por si alguien sabe que ocurre y puede hecharme una mano. No sé si es problema de variables, de la función o que le pasa.
Gracias y un saludo a todos.

#include <LiquidCrystal.h>
static float odometro=0.0; // Km totales
static float km_parc=0.0; // Km parciales
int tinicial=0; //instante en el que se recibe un pulso (bajo) de velocidad (inicio vuelta de rueda)
int tfinal=0; // instante en el que se recibe el siguiente pulso (bajo) de velocidad (tfinal-tinicial=1 vuelta completa)
int tparado=0; // variable que si supera cierto valor pone la velocidad del cuadro a 0
int varVel=0; // variable para contar una vuelta de rueda en la funcion velocidad

int vel=0; // variable que almacena la velocidad
int velMax=0; // variable que almacena la velocidad maxima alcanzada
int perimetro=1805; // perimetro de la rueda en mm (para calculos de velocidad y distancias)

void setup(){
  Wire.begin();
  Serial.begin(9600);
  lcd.begin(16, 2);
}

void loop(){

attachInterrupt(0, velocidad, LOW);

// Velocidad en pantalla principal
  lcd.setCursor(0,0);
   if(vel<100)
    {
      lcd.print(" ");
      if(vel<10)
        {
        lcd.print(" ");
        }
    }
  lcd.print(vel,DEC);
  lcd.setCursor(4,0);
  lcd.print("Kmh");

// Km parciales en pantalla principal    
	lcd.setCursor(6,1);
        if(km_parc<1000)
        {
          lcd.print(" ");
          if(km_parc<100)
          {
            lcd.print(" ");
            if(km_parc<10)
            {
              lcd.print(" ");
            }
          }
        }
      lcd.print(km_parc,0); // muestra los km parciales
      lcd.print("Km");
}
  
/* CALCULO DE LA VELOCIDAD
a esta fulcion se entra cada vez que el sensor detecte un nivel bajo, es decir al
iniciar la vuelta de la rueda y al finalizarla (dos veces por vuelta) */
void velocidad ()
    {
      if(varVel==0) //entra si es el inicio de la vuelta y toma el tiempo actual
      {
        tinicial=millis();
        varVel=1;
      }
      else if (varVel==1) //si es el final de la vuelta resta el tiempo actual al inicial
      // para calcular el tiempo transcurrido entre los 2 pulsos (1 vuelta completa)
      {
        tfinal=millis()-tinicial;                       
        varVel=0;                                       
        vel=perimetro/tfinal;
        vel=vel*3.6;
        
        km_parc=km_parc+(perimetro/100000.0);   
        if(km_parc>1200)
          km_parc=0;   
        odometro=odometro+(perimetro/100000.0);   
        if(odometro>100000)
          odometro=0;
        revision=revision-(perimetro/100000.0); // a esta interrupcion entra 1 vez por vuelta,
        // por lo que le restamos el perimetro de la rueda
      }
    }

afcabanas:
Estas seguro que el perimetro es 1805mm?, eso da casi 2 metros no?

podrias poner que numeros son los que registra, cabe la posibilidad de que el sensor falle en algunas lecturas, por lo que el tiempo seria el doble o el triple y al calcular la velocidad pues parece que fluctue.

puedes usar algun tipo de logica respecto al tiempo leido, algo como que el tiempo leido tiene que estar entre un porcentaje del tiempo anterior o algo asi. o puedes tomar 5 valores y calcular la media.

vamos que creo que lo que falla es que el sensor a veces no registra la lectura.

mvaldebenitorojas
Si, date cuenta que hablamos del perímetro, no del diámetro

Sergegsx
parece buena cosa la de tomar varias lecturas por vuelta y calcular la media. De todas forma, ahora me conformo con que registre la velocidad, aunque esta no sea exacta. El sensor si da todas las lecturas, lo he comprobado mediante un led que se enciende al cortar la señal. Por ejemplo, si no se produce ninguna interrupción, el display debería indicar 0, sin embargo indica el último valor mostrado.

Voy a ver el tema de los tipos de variables, tal vez attachInterrup no admita ciertos tipos o no conserve los valores al salir de la funcion.
Gracias por las sugerencias, seguiremos probando

lo tienes conectadoal pin 2?

afcabanas, por qué tienes la interrupción activada por nivel en vez por flanco?

Por otro lado. las variables que son modificadas por la interrupción y que usas fuera de la misma las tienes que declarar como volatile (http://arduino.cc/es/Reference/Volatile).
El perímetro, podrías usar un #define, en vez variable que consume RAM (ésto es por ser quiscolloso).
Yo haría las menos operaciones posibles dentro de la interrupción, como vas a mostrarlo por lcd, que lo actualizarás cada ¿200 ms?, tienes tiempo de sobra para hacerlo fuera.... ¿no? Podrías ir ampliando una variable en la interrupción, y cada los ¿200ms? (por poner un tiempo), calculas la velocidad, ya que sabes el número de pulsos que tienes en esa cierta cantidad de tiempo.

¿Has copiado y pegado de parte de código? Hay una variable revision y un Wire.begin que despistan un poco.... :wink:

¿Por que sería mejor activarla por flanco?

Viva el codigo libre.

Hola,

Hoy he estado montando un inventillo con un trafo, y necesitaba medir la frecuencia de la red eléctrica.Me he creado este pequeño programa y me he acordado de este post.Uso interrupciones, y es similar a lo que necesitas. Por cierto,si alguien necesita tener controlado el paso por cero de la senoidal de la red eléctrica (por ejemplo para el control de triacs), también le sirve este código.

Pongo por aquí el código por si te ayuda en algo:

volatile unsigned long tanterior;
volatile unsigned long tnuevo;
volatile float frec;

void setup() 
{
  Serial.begin(19200); 
  pinMode(2,INPUT);
  pinMode(13,OUTPUT);  //Led
  attachInterrupt(0,myfunc,CHANGE);
}

void loop() 
{
  Serial.println(frec);
}

void myfunc()
{
  if (digitalRead(2))
  {
    tanterior=tnuevo;
    tnuevo=micros();
    frec=1000000.0/(tnuevo-tanterior);
  }
  digitalWrite(13,!digitalRead(13));  //Led
}

La salida digital (dónde esta conectado el led de Arduino) la cambio de estado cada vez que entro a la rutina de interrupción sólo con fines de debug, ya que conecto el analizador lógico para ver que todo esta funcionando como debería.

Acerca de la pregunta de uno de los post por qué no puede ser LOW... entraría a la interrupción todo el rato que dure el pulso bajo (ya que es activación por nivel). Lo que te interesa es entrar una sólo una vez, para capturar el tiempo.

Hace tiempo escribí este post para cálculo de rpm de un coche => Tinkering with Electronics...: Shift Lights con Arduino

Saludos :wink:

Igor R.