Tacómetro con Arduino

Cómo andan? Ando necesitando diseñar un tacómetro con Arduino el cual pueda medir velocidad menores a 100rpm con una resolución de 1rpm. El tema es que estuve haciendo uno pero la resolución es malísima y no sé por qué. Paso al código:

Código:
int ledPin = 13; // IR LED conectado al pin 13
volatile byte rpmcount;
unsigned int rpm;
unsigned long timeold;

void rpm_fun()
{
rpmcount++;
}

void setup()
{
//Interrupt 0 esta en el pin 2 del arduino, es donde esta conectado el detector de IR
//La interrupcion se activa por FALLING, cuando va de Alto a bajo
attachInterrupt(0, rpm_fun, FALLING);
pinMode(ledPin, OUTPUT);//se activa el led IR
digitalWrite(ledPin, HIGH);
rpmcount = 0;
rpm = 0;
timeold = 0;
}

void loop()
{
delay(1000);//se actualiza el RPM cada segundo
detachInterrupt(0);//no entrar en las interrupciones durante el calculo de RPM
rpm = 30*1000/(millis() – timeold)*rpmcount;
timeold = millis();
rpmcount = 0;
attachInterrupt(0, rpm_fun, FALLING);//Reinicia la funcion de Interrupcion
}

Estoy simulando el proyecto con un motor paso a paso en cuyo eje hay instalado un encoder de 10 ranuras que son detectadas con un sensor de ranura y lo qué hago es, mediante una interrupción, contar la cantidad de ranuras detectas en 1 segundo... aplico fórmula y obtengo el resultado en un display. La cosa es que el error es de como unas 10rpm por cada ranura y no lo puedo corregir.

¿El método para baja velocidad está bien aplicado o debería implementar otro?

Muchas gracias!!!!

p.d.: adjunto una foto del motor con el encoder
 

Adjuntos

  • IMG_20161118_155009.jpg
    IMG_20161118_155009.jpg
    78.3 KB · Visitas: 35
no me gusta tu método, deberías usar pulseIn() y leer el ancho del pulso, luego, la velocidad será proporcional al ancho, de esta forma la actualización no se realiza contando pulsos, si no leyendo cuanto tarda en pasar cada pulso.

Cual seria la formula? bueno, el ancho del puso suponiendo que entre abierto y cerrado tiene la misma longitud de arco, entonces deducimos que para 1RPM la duración del pulso seria 1s/(10ranuras+10paredes)= 50ms para 1RPM, ahora para X RPM seria cosa de dividir 50ms entre al ancho capturado, asi si se captura un pulso de 25ms entonces 50ms/25ms=2 RPM si se captura uno de 5ms seria 50/5=10 RPM, si se captura uno de 200ms 50/200=0,25 RPM, fácil :D, al menos en teoría :p

El método de hacerlo por cuenta no creo que te sea útil con tan baja velocidad, el problema es que necesitas muchas muestras para hacerlo con precisión.
 
y que tal contar el numero de pulsos en un tiempo determinado

digo dentro un timer y cuando el timer se acaba ver cuantos pulos, digo 1 segundo entre 4 son 250ms
contar el numero de pulsos en ese tiempo.


usar delays es un crimen y mas en un arduino que es la sopita maruchan de los microcontroladores
 
Para medir bajas revoluciones, mide el periodo y saca la inversa.
Simplemente pones una interrupción del sensor de rpm y miras cuantos ticks han pasado. Son tres instrucciones.
Así sabes las rpm a cada vuelta y no a cada segundo. Luego decides si envías el dato o no.
Tienes que poner un comprobador de desborde de tiempo porque si se para el motor no te das cuenta, piensas que las rpm son las últimas que viste.

Y sobre todo y para todo borra de tu mente la instrucción delay. Lo estropea TODO.

 
Última edición:
Juazzz.... Casualmente por ésta situación fue que deje de lado el lenguaje/entorno del Arduino, programando un velocímetro no pude controlar decentemente el tiempo de barrido de unos led display siete segmentos, aun menos leer el módulo ICP y programar el TIMER1 por desborde.

Termine aprendiendo Avr-gcc, que me fue mucho más útil, porque tengo control pleno sobre el microcontrolador, o sea las placas Arduino las continuo empleando, pero las programo directamente en "C".

El asunto está hartamente explicado en diversos sitios, pone en el buscador
"avr-gcc + speedometer" o
"avr + tachometer + digital"
y yerbas así, aparecen hasta códigos para que los estudies.

El problema con el Arduino es que su "lenguaje/entorno" solo te da la posibilidad de emplear dos interrupciones las externas INT0 INT1 y ahí se termino... Dejando de lado el uso de las demás interrupciones y sus circuiteria/módulos asociados, por ejemplo el ICP.

En resumen el problema tuyo se puede resolver de la siguiente manera, programas el ICP su configuración seguramente con divisor de 1024, porque tus tiempos son grandes, capturas su disparo con su correspondiente interrupción y te fijas cuantas cuentas logró el timer1, por si hay desborde tambien hay que habilitar la interrupción por desborde del timer1, sumas el asunto en unos contadores que programaste y tenes el tiempo de giro de ese motor, porque al fin y al cabo lo que medís es el tiempo entre dos flancos.

Según vi se puede hacer mezclando un poco de código del avr-gcc con el lenguaje del arduino. Como sensores te recomiendo emplear uno de efecto hall completamente digital y con schmitt trigger.

También sería bueno que busques el código del velocímetro de Amanda Ghassaei, en Instructables, Ella hace un velocímetro para la bici con un Arduino, busca "bike speedometer + Amanda Agassaei" y lo tenés, conste que Ella encara el asunto con otro truco, pero al fin logra el cometido.

Un consejo, para cosas serias me iría olvidando del lenguaje del Arduino y empezaría a estudiar como programat en C los micros de Atmel, para seguir empleando la tremenda cantidad de sensores y placas que existen ahora.
 
Pues yo lo he hecho usando los tics del sistema, cada vez que llega una interrupción del sensor resta medida anterior de medida actual y va bastante bien. hay de milisegundos y de microsegundos, a elegir.
Eso si, cada vez que desborda el contador de tics que es un int32, creo, hará una cosa rara, pero eso es una vez cada bastante tiempo. Se puede corregir por software pero no tenía ganas.

Hay librerías para el uso de los timers y sus interrupciones desde el entorno arduino, Antes había que poner un parche con un trozo de código gcc, pero ya no hace falta.
Lo raro/absurdo es que eso sea opcional y los delays "de serie"

Y si, con ganas y conocimientos es comprensible ignorar el entorno arduino que está lleno de atajos cutres.
 
Última edición:
Tomado de
http://www.instructables.com/id/Measure-RPM-DIY-Portable-Digital-Tachometer/?ALLSTEPS

lo bonito de su rutina es el uso de la función millis() ... Obten el tiempo actual
Para calcular las RPM actuales, necesitas el periodo de la señal de tu sensor.
la función (millis() - tiempo_anterior) nos da ese tiempo.

donde tiempo_anterior es una medida anterior de millis

En tu caso, Sea t = el periodo de la señal de entrada,
....................... REV = el número de pulsos recividos durante ese periodo

Así que el número total de revoluciones en 60sec ( 60* 1000 millisecond ) es :

....... rpm = 60*1000 / {(t)* REV} =>
....... rpm = 60*1000 / (millis() - tiempo_anterior ) * REV/(número de slots x revolución)
....... como tienes 10 ranuras en tu sensor
....... rpm = 6*1000 / (millis() - tiempo_anterior) * REV;

Este otro tutorial esta mejor y usa una arandela ranurada con 20 secciones
https://brainy-bits.com/tutorials/speed-sensor-with-arduino/
 
Última edición:
Buenas tardes chicos. Vuelve un hijo perdido jeje.

Tengo un problema con un tacometro basado en arduino; estoy utilizando el sketch de Nick Gammon

Tengo lecturas muy estable "casi" todo el tiempo. Quisiera saber si podrian ayudarme a saber si el problema es de hardware.

Acá esta el circuito que estoy usando.

De antemano, muchas gracias.
 

Adjuntos

  • imagen_2021-01-18_155813.png
    imagen_2021-01-18_155813.png
    15.8 KB · Visitas: 26
No entiendo cual es la duda. ¿ La lectura cambia mucho, poco, no cambia ? ¿ Que es lo que esta sensando ? ¿Cual es el resultado obtenido y cual el que quieres obtener ?, porque, al menos a mi, no me queda claro.

Suena a que te falta mas promedio en las lecturas, pero puede ser otro el problema.

¿ Frecuencia a medir ? ¿ Tensión de la señal ?

¿ Porque la medida esta en la salida de Q1 y no en la puerta ? ¿ Quien controla a Q1 ?

Cuanto mas datos, mejor van a ser las respuestas.
 
Gracias por responder switchxxi.

Estoy intentando hacer un tacometro para mi auto. Para ejecutar ciertas acciones en consecuencia según las rpm medidas. La medición la estoy tomando en la señal de entrada de la bobina del motor.

La bobina (la gran mayoría del sector automotor) funciona conmutando el negativo. Resulta que en ocasiones tengo lecturas de rpm muy altas en una lectura, con lo cual me altera la toma de decisiones. En la imagen se ve un caso, pero aveces son incluso mas altas; 20000 incluso.

El pin de entrada del arduino esta en el colector de Q1 para aislar en cierta medida al arduino del lado de 12v, y asegurar que siempre tengo una logica clara. Cabe aclarar que si bien el lado de baja de la bobina trabaja con 12Vdc, pueden generarse hasta 190V de pico inductivo.
 

Adjuntos

  • WhatsApp Image 2021-01-18 at 12.48.50 PM.jpeg
    WhatsApp Image 2021-01-18 at 12.48.50 PM.jpeg
    90.7 KB · Visitas: 15
Por lo pronto, esta es la etapa de entrada de un tacómetro comercial: Diagrama. (Ahora me doy cuenta que le va a faltar la resistencia de pull up también en ese circuito).

Lo raro es que las medidas siguientes parecen concordar con una aceleración.

¿ Código usado ? ¿ Esquema armado ?

¿ Alimentación del circuito ? Quizás haya demasiado ruido. ¿ El cable esta bien conectado a la bonina ? Si solo esta con una vueltas, con el movimiento del motor puede hacer falso contacto y eso suma toneladas de RPM XD.
 
switchxxi, yo si tengo pull up, esta a la salida del 555 que también alimenta al arduino.

Si, en ese momento estaba acelerando el motor, pero nunca pase de 2500 rmp comparando con el tablero, siempre estuve probando en ralentí y poco aceleración mas. El esquema que arme, en efecto es el de la imagen.

Ya me había percatado del problema del ruido y tome la señal directamente en una de las salidas de la ECU que comandan las bobinas (son dos, trabajan por chispa perdida).

El codigo:
Código:

// Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

// here on rising edge
void isr ()
{
unsigned int counter = TCNT1; // quickly save it

// wait until we noticed last one
if (triggered)
return;

if (first)
{
startTime = (overflowCount << 16) + counter;
first = false;
return;
}

finishTime = (overflowCount << 16) + counter;
triggered = true;
detachInterrupt(0);
} // end of isr

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
overflowCount++;
} // end of TIMER1_OVF_vect


void prepareForInterrupts ()
{
// get ready for next time
EIFR = _BV (INTF0); // clear flag for interrupt 0
first = true;
triggered = false; // re-arm for next time
attachInterrupt(0, isr, FALLING);
} // end of prepareForInterrupts


void setup () {
Serial.begin(19200);
Serial.println("Frequency Counter");

// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
// Timer 1 - interrupt on overflow
TIMSK1 = _BV (TOIE1); // enable Timer1 Interrupt
// zero it
TCNT1 = 0;
// start Timer 1
TCCR1B = _BV (CS10); // no prescaling

// set up for interrupts
prepareForInterrupts ();

} // end of setup

void loop ()
{
if (!triggered)
return;

unsigned long elapsedTime;
float freq;
int rpm;
elapsedTime = finishTime - startTime;
freq = 1.0 / ((float (elapsedTime) * 62.5e-9)); // each tick is 62.5 nS
rpm = freq*60;
Serial.print ("Took: ");
Serial.print (elapsedTime);
Serial.print (" counts.\t");

Serial.print ("Frequency: ");
Serial.print (freq);
Serial.print (" Hz.\t");
Serial.print ("RPM: ");
Serial.println (rpm);


prepareForInterrupts ();
} // end of loop

Estaba utilizando uno similar al de ingeniero18, al comienzo este post, pero es ineficiente al momento de medir las rpm de manera precisa cuando estas en momentos de aceleración o desaceleración.
 
No entiendo el código, pero eso ya es porque no he programado en arduino mucho, solo un par de cosas para probar. Por lo pronto entiendo y me parece que se mide el periodo entre pulsos de bajada y luego se calcula la frecuencia. En un principio pareciera estar bien el programa, por lo que vuelvo a la pregunta de antes ¿ Como conectas el cable que sensa las RPM, pinza cocodrilo, pinchado con un alfiler, un gancho de prueba, etc ?

Si armas un simple oscilador con un 555, ¿ mide bien y parejo ?
 
Si utilizo un 555, va bien; si armo un oscilador con transistores, va bien.

Así estoy tomando la señal:

Aunque estoy pensando soldar el cable mientras hasta que finiquito el proyecto
 

Adjuntos

  • IMG_20210120_001136.jpg
    IMG_20210120_001136.jpg
    205.2 KB · Visitas: 13
En un principio ese puede ser un gran problema ya que si el cable no esta lo suficientemente firme al otro cable va a haber falsos contactos y si hay falsos contactos se van a generar falsos pulsos y si se generan falsos pulsos... Pues... Creo que ya estas calculando que puede pasar con las RPM.
 
Bueno, al parecer este proyecto pudo más que yo. Acá debajo dejo todos los dstos de qué y cómo lo arme.

Con ese circuito tengo lecturas mas claras por mas tiempo, pero sigo teniendo algunas lecturas erráticas a veces. Incluso, pierdo comunicación entre el arduino y la pc.

Lleve todo dentro del auto y traigo el cable de "señal" desde la cajuela hasta el circuito de condicionamiento.

Además, cuando logro tener comunicación entre el cacharro y la pc, e intento hacer que haga el 'corte' me corta la el relay de la bobina y no lo activa nuevamente.

El relay lo coloco seccionando el cable de positivo directo del swicht.

Además, intente colocar el trozo de código que lleva (o debería llevar) a cabo tal fin, antes, dentro y después del código de lectura, pero simplemente se niega a funcionar.

A parte de rendirme, que mas me aconsejan?
Mensaje automáticamente combinado:

Pd: Si, es un launch, al que le quiero anexar mas funciones. Pero como sea no logro si quiera hacer que cuente correctamente las rpm.
Acá el código:

// Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

int bobina = 5;
int rpm_corte = 0;
int boton_launch = 13;

unsigned int rpm =0;

// here on rising edge
void isr ()
{
unsigned int counter = TCNT1; // quickly save it

// wait until we noticed last one
if (triggered)
return;

if (first)
{
startTime = (overflowCount << 16) + counter;
first = false;
return;
}

finishTime = (overflowCount << 16) + counter;
triggered = true;
detachInterrupt(0);
} // end of isr

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
overflowCount++;
} // end of TIMER1_OVF_vect


void prepareForInterrupts ()
{
// get ready for next time
EIFR = _BV (INTF0); // clear flag for interrupt 0
first = true;
triggered = false; // re-arm for next time
attachInterrupt(0, isr, FALLING);
} // end of prepareForInterrupts

void launch()
{
if(digitalRead(boton_launch) == LOW)
{
digitalWrite(bobina, HIGH);
}

if(rpm >= rpm_corte && digitalRead(boton_launch) == HIGH)
{
digitalWrite(bobina, LOW);
}

if(digitalRead(boton_launch) == HIGH && rpm < rpm_corte)
{
digitalWrite(bobina, HIGH);
}

if(digitalRead(boton_launch) == LOW)
{
digitalWrite(bobina, HIGH);
}
}


void setup () {
Serial.begin(19200);
Serial.println("Frequency Counter");
pinMode (bobina, OUTPUT);
pinMode (12,OUTPUT);
digitalWrite (bobina, HIGH);
digitalWrite (boton_launch,INPUT);

rpm_corte = map (analogRead(A5), 0, 1023, 1000, 10000);
Serial.print("corte "); Serial.println(rpm_corte);

digitalWrite(12,HIGH);
delay(5000);
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
// Timer 1 - interrupt on overflow
TIMSK1 = _BV (TOIE1); // enable Timer1 Interrupt
// zero it
TCNT1 = 0;
// start Timer 1
TCCR1B = _BV (CS10); // no prescaling




// set up for interrupts
prepareForInterrupts ();



} // end of setup

void loop ()
{
if (!triggered)
return;

unsigned long elapsedTime;
float freq;
elapsedTime = finishTime - startTime;
freq = 1.0 / ((float (elapsedTime) * 62.5e-9)); // each tick is 62.5 nS

///////////////////////////////////////////////////////////
rpm = freq*60;
// Serial.print ("Took: ");
//Serial.print (elapsedTime);
//Serial.print (" counts.\t");

Serial.print ("Frequency: ");
Serial.print (freq);
Serial.print (" Hz.\t");
Serial.print ("RPM: ");
Serial.println (rpm);

prepareForInterrupts ();

launch();

} // end of loop
 

Adjuntos

  • IMG-20210122-WA0014.jpg
    IMG-20210122-WA0014.jpg
    51.3 KB · Visitas: 15
  • IMG-20210122-WA0012.jpg
    IMG-20210122-WA0012.jpg
    64.4 KB · Visitas: 14
  • IMG-20210122-WA0013.jpg
    IMG-20210122-WA0013.jpg
    46.2 KB · Visitas: 15
Última edición:
Incluso, pierdo comunicación entre el arduino y la pc.

No se ve como esta echo el circuito en el protoboard, pero quizás entre demasiado ruido en la alimentación.

Lleve todo dentro del auto y traigo el cable de "señal" desde la cajuela hasta el circuito de condicionamiento.

Los tacómetros comerciales usan un simple cable sin problemas para llevar la señal de las revoluciones.

Además, cuando logro tener comunicación entre el cacharro y la pc, e intento hacer que haga el 'corte' me corta la el relay de la bobina y no lo activa nuevamente.

El relay lo coloco seccionando el cable de positivo directo del swicht.

Además, intente colocar el trozo de código que lleva (o debería llevar) a cabo tal fin, antes, dentro y después del código de lectura, pero simplemente se niega a funcionar.

C:
void launch()
{
    if(digitalRead(boton_launch) == LOW)
        {    digitalWrite(bobina, HIGH);    }

    else if(rpm >= rpm_corte && digitalRead(boton_launch))
        {    digitalWrite(bobina, LOW);    }

    else if(rpm < rpm_corte && digitalRead(boton_launch))
        {    digitalWrite(bobina, HIGH);        }

    else
        {    digitalWrite(bobina, HIGH); }
}

Prueba a ver que pasa así.

A parte de rendirme, que mas me aconsejan?

Seguir intentando, estas son las cosas que ayudan a aprender muchísimo. Buscar los problemas y resolverlos.

Siempre trata de resolver un problema a la vez, si intentas hacer que todo ande te volverás loco.

Por lo pronto, arduino en si son dos microcontroladores o, mas bien, el micro principal y el chip encargado de la comunicación que en algunos es otro microcontrolador. Quizá la falta de comunicación no se deba porque el micro principal tuvo un problema, puede deberse al de comunicación.

Si tienes osciloscopio se te va a hacer mucho mas fácil, pero no 100% es necesario. En cambio pon un led en algún pin y hazlo titilar, si la comunicación falla pero el led sigue titilando ya puedes descartar y achicar los problemas.

Ademas, al tener un arduino conectado a una PC, se puede usar los Serial.print para saber que cosa esta haciendo el arduino, usa tantos como creas conveniente (luego habra que sacarlos todos una vez ande bien el proyecto, pero es una muy buena herramienta).
 
Última edición:
La fuente es un 7805 (en rojo) solo para el arduino. Los filtros del lado de 5v están justo al lado del arduino(rojo también), justo para intentar mitigar cualquier ruido, pero no descartó que puedan seguir estando presentes, aunque ahora me que me percato, no tengo filtros para la entrada del regulador.

Además de eso, solo tengo una pull down (azul) para el pulsador y el led para indicarme el estado de la bobina(verde).

Un pot (negro) con el que seteo las rpm de corte en el setup y no tener código muerto en el loop.

Y el transistor para comandar el relay (amarillo).

Lamentablemente no tengo un osciloscopio; me encantaría.

De todas maneras en la noche, al salir del trabajo, resuelvo lo de los filtros de entrada y te hago un esquema.

Por cierto, si entiendes mis esquemas? En el colegio mi profesor de electrónica no los comprendía. Jeje
 

Adjuntos

  • Polish_20210123_095618104.jpg
    Polish_20210123_095618104.jpg
    326.1 KB · Visitas: 5
Última edición:
Atrás
Arriba