Hola, estoy tratando de medir la RPM de un motor DC pequeño a través del MCU ESP32, sensor optico ITR8102 y encoder de 20 ranuras.
El programa que tengo funciona muy bien para el Arduino uno pero no para el esp32, les presento el codigo que funciona con Arduino:
/*Arduino Uno
Medición RPM con sensor ITR8102 y encoder 20 ranuras*/
volatile int contador = 0;
volatile int rpm = 0;
void setup() {
Serial.begin(57600);
attachInterrupt(0,rutina,FALLING);
}
void loop() {
delay(1000);
rpm=(60*contador)/20;
Serial.println(rpm);
cont=0;
}
void rutina(){
contador=contador+1;
}
Este código lo quise usar para el ESP32 y no funciono, el primer problema fue que no contaba bien las ranuras del encoder por que tenía mucho rebote (debounce). Después de haber investigado solucione que el sensor óptico contara de forma correcta las ranuras del encoder.
con el siguiente código el sensor cuenta las ranuras de forma correcta:
/*ESP 32
Contador sensor óptico ITR8102 y encoder 20 ranuras*/
volatile int contador = 0; //variable que se ejecutara al hacer la interrupción
int sensor = 23; // pin que uso para hacer interrupción
volatile unsigned long tiempoDeInterrupcionAnterior = 0;
#define tiempoDeRebote 200
void IRAM_ATTR interrupcion(); // es para que la funcion se guarde en la memoria ram y no en la flash
void setup() {
pinMode(23,INPUT);
Serial.begin(115200);
attachInterrupt(23,interrupcion,FALLING); // utilizo IO23 de interrupción
}
void loop(){
/*Al introducir esta formula que funciono con el Arduino uno,
en el ESP 32 me entrega datos diferentes.
delay(1000);
rpm=(60*contador)/20;
Serial.println(rpm);
cont=0;
explicación de la formula:
Con un retardo de 1 segundo automáticamente la variable contador
va a guardar el numero de interrupciones en ese segundo y
se multiplica por 60 entre el numero de ranuras que es 20 en mi caso.
*/
}
void IRAM_ATTR interrupcion(){
if(millis() - tiempoDeInterrupcionAnterior > tiempoDeRebote){
contador = contador+1;
//Serial.println(contador);
tiempoDeInterrupcionAnterior = millis();
}
}
Agradecería cualquier comentario sobre este fallo, por que en Arduino Uno funciona bien y en el ESP-32 obtengo datos distintos, Saludos.
Hola ruilviana gracias por comentar, alimente el sensor óptico ITR8102 CON 3.3V del ESP-32 "para evitar dañarse" y cuenta bien las ranuras del encoder.
Sin embargo las RPM sigo obteniendo los mismos resultados. Muestro a continuación una tabla de comparación del Arduino y ESP32.
Se logra apreciar que la lectura que hago del Arduino Uno coincide con el tacometro digital. Aun no se cual sea la razón por que no es el mismo resultado si de un lado el código funciona muy bien y para el otro no, a lo mejor el ESP-32 aplica otra formula, pero voy a seguir investigando para saber por que las lecturas del ESP-32 siempre me dan esos números, saludos y otra vez gracias por el consejo.
Hola @X_rramirez5411 .
Fui aquí para probar su boceto para ESP32 y di un error de compilación.
Queda por definir las variables "rpm" y "cont".
Busqué en el boceto para arduino y encontré la variable "rpm" pero no encontré la variable "cont".
RV mineirin
lo siento, ese fue un error mio que se me fue de uno de los borradores que hice, este es el codigo bueno para el ESP-32:
volatile int contador = 0; //variable que se ejecutara al hacer la interrupción
volatile int rpm = 0;
int sensor = 23; // pin que uso para hacer interrupción
volatile unsigned long tiempoDeInterrupcionAnterior = 0;
#define tiempoDeRebote 200
void IRAM_ATTR interrupcion(); // es para que la funcion se guarde en la memoria ram y no en la flash
void setup() {
pinMode(sensor,INPUT);
Serial.begin(115200);
attachInterrupt(sensor,interrupcion,FALLING); // utilizo IO23 de interrupción
}
void loop(){
delay(1000);
rpm=(60*contador)/20;
Serial.println(rpm);
contador=0;
}
void IRAM_ATTR interrupcion(){
if(millis() - tiempoDeInterrupcionAnterior > tiempoDeRebote){
contador = contador+1;
//Serial.println(contador);
tiempoDeInterrupcionAnterior = millis();
}
}
Hola @X_rramirez5411 .
mira si este esquema funciona como necesitas
La rutina debugTest no es necesaria.
Produce una cadena de pulsos en lo pin 2 que se pueden inyectar en lo pin 23 para simular.
Solo haz un puente de 2 a 23.
Si no desea usarlo, puede eliminarlo, pero comente la línea que lo llama en setup ().
Lo que encuentro yo es que el optoacoplador requiere mas corriente de la que esta recibiendo a 3.3V.
El arduino puede al tener salida de 5V lo logra pero el ESP32 a 3.3V no alcanza.
Veamos dice que con una If de 20mA tienes una caida en el diodo de 1.2V (ver hoja de datos)
Para 5V la cuenta con una R de 220ohms da
I = (5 -1.2)/220 = 17.2mA
pero para 3.3V la R debe ajustarse
R = (3.3 -1.2)/20mA = 105 ohms
Debes usar una R de 105 o 120 ohms incluso una de 100 ohms para que funcione correctamente en el ESP32
Prueba y nos cuentas.
a la primer línea de loop() para que el delay() ocurra inmediatamente luego de ponerlo a 0.
El tiempo de anti rebote en la interrupción del código de los posts #1 y #5 me parece muy largo, creo que 200 mseg es exagerado y se dejan de contar pulsos entre tanto.
Tomando el ejemplo de 139 RPM
139 / 60 seg da casi 2.32 RPS.
2.32 × 20 ranuras generan unos 46 pulsos.
1/46 da unos 21.7 mseg entre pulsos.
Quiero decir que si tomas como base esa rutina de interrupción, desde mi punto de vista no te va a funcionar (aunque no se cuál código finalmente estás depurando).
Hola gatul, los códigos del post #1 y #5, les coloque interrupción debido a que lo primero que necesitaba es comprobar si estaba contando bien las ranuras del encoder, al principio se saltaba la cuenta porque había mucho rebote y lo solucione con 200 milisegundos de espera.
Despues en el post #7 ruilviana me proporciono otro codigo sin interrupciones que me daba otros datos que a los del post #5 (en mi codigo solo obtenía 3 números y en el del post#7 proporcionaba datos más reales que son los del ejemplo 139 e hice un cambio de resistencia de 100 en lugar de 220 Ohms), sin embargo, no se acercaba a los datos del tacómetro digital.
En el Arduino funcionaba muy bien el codigo, yo creo que debe ser un problema de ajustar resolución del ESP32, desconozco de ese tema, tengo que investigarlo, saludos gatul y gracias por contestar.
No se porque repites a cada momento la palabra resolución!!
Estas contando pulsos. No usas un ADC donde si tiene sentido lo que dices, los 10 bits del Arduino contra los 12 del ESP32.
Tiene un gran defecto y es que los pulsos deben contarse en una ventana de tiempo. En tu caso el delay de 1000 mseg.
Pones el delay pero el sistema sigue contando.
Tienes que contar en medio de la ventana de tiempo, luego detener las interrupciones, poner a 0, presentar y volver a activar las interrupciones para el proximo ciclo de lectura.
Porque si no habilita las interrupciones antes del println() va a haber problemas con Serial.
De todos modos, en el rango de revoluciones que está midiendo no me parece que sea absolutamente necesario deshabilitar las interrupciones.
A lo sumo podrá tener un error de un pulso por medición, por lo que le marcaría unas 3 RPM de más, muy de vez en cuando.
Surbyte, la razón de por que comentaba mucho la palabra resolución era por este código, lo que tiene diferente al código que yo presente son dos cosas:
1- La parte void debugTest().
2- En la interrupción no existe tiempo de rebote.
Y sus resultados se me hacen mejores que al mio aunque un poco alejados a los del tacometro digital.
Probá con tiempos de antirrebote entre 10 a 15 mseg. a ver como se comporta.
Si tenes pulsos cada 20 mseg (para redondear) y vos pones un antirrebote de 150 mseg, 6 pulsos no los vas a leer porque los filtras como si fuesen rebotes cuando no lo son.
Agrego:
¿Pero por qué el código trabaja bien con el arduino y no con el ESP?
Viendo las hojas de datos de los micros se entiende más fácilmente.
Para el ATMega un nivel alto tiene que ser superior a 0.6×Vcc, o sea que para una alimentación de 5V, el nivel se toma como alto a partir de 0.6×5V = 3V.
El nivel es bajo cuando está por debajo de 0.3×Vcc, o sea 0.3×5V = 1.5V.
Sin embargo para el ESP32 los niveles son:
Alto a partir de 0.75×Vcc entonces por encima de 0.75×3.3V = 2.475V se toma como alto.
El nivel bajo tiene que estar por debajo de 0.25×Vcc, o sea, 0.25×3.3V = 0.825V
Esa diferencia en los niveles de tensión para cada estado es lo que hace que una señal (en este caso, un posible rebote) que para el arduino es invisible porque no está por encima/debajo de los niveles alto/bajo, el ESP lo vea mucho más fácilmente como un pulso.
Por ejemplo, un rebote con una tensión de 2.8V el arduino no lo ve, por lo que no verá tampoco el flanco de caída de ese rebote. En cambio el ESP si lo detecta (porque pasa los 2.475V) y claramente detectará su flanco de bajada generando una interrupción.
Hola gatul, tome lectura de voltaje en la IO23 y es de 3.1 V, significa que si esta entrando el voltaje adecuado.
Realicé la prueba de disminuir el tiempo de rebote como me indicaste y el resultado fue satisfactorio (Coinciden los datos del ESP32 y tacómetro digital), este es el código en el cual hice la prueba:
volatile int contador = 0;
volatile int rpm = 0;
int sensor = 23;
volatile unsigned long tiempoDeInterrupcionAnterior = 0;
#define tiempoDeRebote 14
void IRAM_ATTR interrupcion(); // es para que la funcion se guarde en la memoria ram y no en la flash
void setup() {
pinMode(sensor,INPUT);
Serial.begin(115200);
attachInterrupt(sensor,interrupcion,FALLING);
//attachInterrupt(digitalPinToInterrupt(23),interrupcion,RISING);
}
void loop(){
//void loop hecho por gatul
contador=0;
delay(1000);
noInterrupts();
rpm=contador*3; // 60/20=3;
interrupts();
Serial.println(rpm);
}
void interrupcion(){
if(millis() - tiempoDeInterrupcionAnterior > tiempoDeRebote){
contador=contador+1;
///Serial.println(contador);
tiempoDeInterrupcionAnterior = millis();
}
}
Resultados obtenidos:
En lo que he investigado del ESP a diferencia del Arduino es que existe mucho rebote al momento de tomar lecturas. No se si sea recomendable hacer un filtro para que tome lecturas más exacto porque a veces existe rebotes de lectura de ±3 rpm.