Tacómetro solo muestra de 60 en 60

Buenas tardes foreros, antes de nada presentarme. Soy programador novato y recién iniciado en la electrónica. Me llamo Sergio y soy de Jaén. Estoy desarrollando un tacómetro para medir las RPM de motores con sensor de efecto Hall y un iman de neodimio. La verdad es que he avanzado bastante, me muestra los valores en la pantalla pero me he dado cuenta que solo los muestra de 60 en 60, me explico… Por ejemplo, de 4320 pasaría a 4380 o a 4260, no mostraría revoluciones intermedias como por ejemplo 4322.

Me he basado en código que he encontrado en internet adaptándolo a lo mio, os lo pego aquí:

#include <LiquidCrystal.h>
//definimos los pines de la pantalla
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);

#define max_rpm 2000 //rpm maximas para mostrar las barras
const int pinSensor = 2; //sensor hall conectado a la entrada digital 2

// declaramos el sensor como apagado
int estadoSensor   = 0;
int estadoAnterior = 0; 

// Variables a Utilizar
int rpm; 
const unsigned long sampleTime=1000;


void setup() 
{    
  Serial.begin(9600);// Puerto
  lcd.begin(20,4);   // iniciamos pantalla
  lcd.print("RPM:");
  pinMode(pinSensor, INPUT);  // el sensor como una entrada 
}
void loop()
{
  estadoSensor = digitalRead(pinSensor);// se lee lo que tiene el sensor y se compara con el valor anterior en este caso sera 0
  if (estadoSensor != estadoAnterior)
  {
     rpm=getRPM();// metodo que se crea mas adelante para configurar las revoluciones por minuto del motor
     lcd.setCursor(0,1);
     lcd.print(rpm);
     lcd.print(" rpm.");
     lcd.setCursor(0,2);
     muestraBarra(rpm);
   }
   estadoAnterior = estadoSensor;  // se reinicia el estado del sensor a 0 para que repita la condicion 
}

// este es el metodo para hallar las revoluciones por minuto de nuestro motor
// se incluye la funcion 
// millis() que es aquella que llevara el tiempo desde que inicia el programa
int getRPM()
{
  int kount=0;
  boolean kflag=LOW;
  unsigned long currentTime=0;
  unsigned long startTime=millis();
  
  while (currentTime<=sampleTime)
  {
    if (digitalRead(pinSensor)==HIGH)
    {  
      kflag=HIGH;
    }
    if (digitalRead(pinSensor)==LOW && kflag==HIGH)
    {
      kount++;
      kflag=LOW;
    }
    currentTime=millis()-startTime;
  }
  int kount2rpm = int(60000.0/float(sampleTime))*kount;
  return kount2rpm;
}

//actualizar o visor
void muestraBarra(int r) {
  int i=map(r, 0, max_rpm, 0, 20);
  for(int n=0;n<20;n++) {
     lcd.print((char)(n<i?255:' '));
  }
}

Supongo que tiene que ver con el sampletime que equivale a 1000 y a los 60000 del calculo de kount2rpm, si los divides sale 60, he probado a cambiar los valores, pero la verdad, se disloca.

¿Alguien me hecha un cable?

Izanx:
Buenas tardes foreros, antes de nada presentarme. Soy programador novato y recién iniciado en la electrónica. Me llamo Sergio y soy de Jaén. Estoy desarrollando un tacómetro para medir las RPM de motores con sensor de efecto Hall y un iman de neodimio. La verdad es que he avanzado bastante, me muestra los valores en la pantalla pero me he dado cuenta que solo los muestra de 60 en 60, me explico… Por ejemplo, de 4320 pasaría a 4380 o a 4260, no mostraría revoluciones intermedias como por ejemplo 4322.

Me he basado en código que he encontrado en internet adaptándolo a lo mio, os lo pego aquí:

#include <LiquidCrystal.h>

//definimos los pines de la pantalla
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);

#define max_rpm 2000 //rpm maximas para mostrar las barras
const int pinSensor = 2; //sensor hall conectado a la entrada digital 2

// declaramos el sensor como apagado
int estadoSensor  = 0;
int estadoAnterior = 0;

// Variables a Utilizar
int rpm;
const unsigned long sampleTime=1000;

void setup()
{   
  Serial.begin(9600);// Puerto
  lcd.begin(20,4);  // iniciamos pantalla
  lcd.print(“RPM:”);
  pinMode(pinSensor, INPUT);  // el sensor como una entrada
}
void loop()
{
  estadoSensor = digitalRead(pinSensor);// se lee lo que tiene el sensor y se compara con el valor anterior en este caso sera 0
  if (estadoSensor != estadoAnterior)
  {
    rpm=getRPM();// metodo que se crea mas adelante para configurar las revoluciones por minuto del motor
    lcd.setCursor(0,1);
    lcd.print(rpm);
    lcd.print(" rpm.");
    lcd.setCursor(0,2);
    muestraBarra(rpm);
  }
  estadoAnterior = estadoSensor;  // se reinicia el estado del sensor a 0 para que repita la condicion
}

// este es el metodo para hallar las revoluciones por minuto de nuestro motor
// se incluye la funcion
// millis() que es aquella que llevara el tiempo desde que inicia el programa
int getRPM()
{
  int kount=0;
  boolean kflag=LOW;
  unsigned long currentTime=0;
  unsigned long startTime=millis();
 
  while (currentTime<=sampleTime)
  {
    if (digitalRead(pinSensor)==HIGH)
    { 
      kflag=HIGH;
    }
    if (digitalRead(pinSensor)==LOW && kflag==HIGH)
    {
      kount++;
      kflag=LOW;
    }
    currentTime=millis()-startTime;
  }
  int kount2rpm = int(60000.0/float(sampleTime))*kount;
  return kount2rpm;
}

//actualizar o visor
void muestraBarra(int r) {
  int i=map(r, 0, max_rpm, 0, 20);
  for(int n=0;n<20;n++) {
    lcd.print((char)(n<i?255:’ '));
  }
}




Supongo que tiene que ver con el sampletime que equivale a 1000 y a los 60000 del calculo de kount2rpm, si los divides sale 60, he probado a cambiar los valores, pero la verdad, se disloca.

¿Alguien me hecha un cable?

si no quieres perder pulsos lo mejor es usar una interrupcion que los detecte :
http://arduino.cc/en/Reference/AttachInterrupt

Tu respuesta esta acá

 int kount2rpm = int(60000.0/float(sampleTime))*kount;

sampleTime = 1000

60000/1000 = 60 por lo tanto no importa que diga tu contador Kount siempre sera de 60 en 60. Tu problema esta en que 4320 son 72 hz y el siguiente valor son 73 Hz o 4380 por lo tanto es la manera en la que mides la que te genera problemas. Tu debes medir período y no frecuencia. Período es la inversa de la frecuencia. Como cambia esto: Pues bien. 72hz son 1/72 = 13.88 mseg 73hz son 1/73 = 13.69 mseg para detectar eso tu deberías contar los useg entre transición 1-> 0, con cada transición tu mides el tiempo que demoró. ejemplo hubieras medido en useg 13888 useg y 13690 useg veamos 1/13.888 mseg = 72.0058 Hz o 4320,276 rpm Lo mismo para el otro caso Ahora si hubieras medido 13887 useg => 1/13888*60000= 4320,59 cambiemos algo mas mides 13880 useg => son 4322 rpm

Que te parece el cambio? Ah por cierto fue mas rápida la lectura asi que si quieres para mejorarla lees varias cambios y promedias.

Gracias por las respuestas, entiendo ámbas soluciones, intentaré trasladarlas a código ya que estoy muy pegado en arduino. No es fácil para mi ahora jaja, pero con el tiempo aprenderé. Gracias.

bueno, al final he optimizado el código, con interrupciones y la función micros en vez de millis… ahora tengo otra duda.

Me gustaría ir capturando el tiempo que el motor está andando.

Había pensado en crear una variable temporal que vaya capturando el valor de millis cuando inicia el movimiento del motor y que cuando pare reste millis actual menos el temporal capturado anteriormente, todo eso ir sumándolo a otra variable para poder mostrarlo en pantalla.

#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);

const int sensor = 2;         // sensor hall (interrupt 0)
const int numAvg = 10;        // número de valores para calcular los promedios
const int numTicks = 1;       // número de imanes en el eje
#define max_rpm 1100          // número máximo de revoluciones del motor
int ejecucion = 0;            // tiempo de funcionamiento del motor
int temporal = 0;
int activo = 0;

float     rpmVal = 0;         // valor actual rpm
float     rpm[numAvg];        // array para las medias
float     rpmAvg = 0;         // media de rpm

unsigned long timeOld = 0;    // tiempo ultima interrupción
unsigned long timePulse = 0;  // tiempo entre interrupción 

void setup() {
  pinMode(3, OUTPUT); // rele 1
  pinMode(4, OUTPUT); //rele 2
  lcd.begin(20,4);   // iniciamos pantalla
  lcd.print("RPM:");
  pinMode(sensor,INPUT);                     // pin 2 como entrada
  attachInterrupt(0, rpm_function, FALLING); 
  Serial.begin(9600);                        
  for (int i = 0; i < numAvg; i++) {         // vector a 0
    rpm[i] = 0;
  }
}

void loop() {
  rpmVal = 60*1000000/(timePulse * numTicks);     // calculamos las rpm actuales
  rpmVal = constrain(rpmVal, 0, max_rpm);            // la establecemos entre 0 y el maximo
  
  if(micros() - timeOld > 1000000) { // motor parado
    rpmVal = 0;
    ejecucion += millis() - temporal; // guardo el tiempo que ha estado funcionando
    activo = 0;
  }

  // calculamos el promedio de rpm
  rpmAvg = 0;
  for (int i = numAvg-1; i >= 0; i--) {
    if (i > 0) {
      rpm[i] = rpm[i-1];
    } else {
      rpm[i] = rpmVal;
    }
    rpmAvg += rpm[i];
  }
  rpmAvg = rpmAvg/numAvg;
  
  // delay "debounces" sensor
  detachInterrupt(0);
  delay(80);
  attachInterrupt(0, rpm_function, FALLING);
 
     lcd.setCursor(0,1);
     lcd.print((rpmAvg),1);
     lcd.print(" rpm   ");
     lcd.setCursor(0,2);
     printVu(rpmAvg);
     lcd.setCursor(0,3);
   
   if (rpmVal==0){
       //parado
       lcd.print("PARADO    ");
       digitalWrite(4, HIGH);   // alterno rele
       digitalWrite(3, LOW);   
   } else {
       //en marcha
       digitalWrite(3, HIGH);   // alterno rele
       digitalWrite(4, LOW);   
       lcd.print("EN MARCHA"); 
       if (activo == 0){
         temporal = millis() ; //tiempo en el que empieza a andar el motor
         activo = 1;
       }
     }
     lcd.print(" - ");
     lcd.print(ejecucion/1000 ); //muestro los segundos que llevan ejecutandose
}

void rpm_function() {
  timePulse = (micros() - timeOld);
  timeOld = micros();
}

void printVu(int r) {
  int i=map(r, 0, max_rpm, 0, 20);
  for(int n=0;n<20;n++) {
     lcd.print((char)(n<i?255:' '));
  }
}

El problema es que se me vuelve loco al mostrar el valor de ejecucion, algo hago mal…

La alternativa interrupción no te va a mejorar en nada tu problema. Por qué? Porque tienes un solo sensor de efecto Hall por ende, cuentas solo 1 vez por giro de la rueda.

Tu problema reside en que la medición de bajas frecuencias es mas precisa cuando se lee por período y la medicion de alta frecuenca es mas precisa si se mide justamente con ventanas de tiempo como la que tiene tu código.

En tu caso para mejorar tu resolución debes aumentar la ventana a cambio de que te resulte mas pesada su visualición. Si aumentas la ventana o sampleTime a 10000 mseg verás cambios cada 6 y si aumentas a 100.000 ahi verás cambios cada 0.6

Otra alternativa es que aumentes la cantidad de sensores de efecto hall. Y he aqui tu problema: Si mides RPM de un motor nunca se pone un solo sensor. Se usa una rueda dentada de N dientes, en particular si fueran 60 leerías directamente las RPM y con tu ventana de 1000 mseg sería suficiente, asi que ya tienes los elementos para mejorar tu problema.

aqui tienes un ejemplo que usa el sensor de un ventilador de pc ,pero es lo mismo,lleva la entrada al pin 2 (interrupcion): http://playground.arduino.cc/Main/ReadingRPM