Measuring RPM using Proximity Sensor

Hello Community,

I am trying to make a project that should determine the rpm of a motor. To do that I am using a 200 rpm geared motor, proximity sensor and arduino clone. Suggest me a code to achieve this. I am currently using this code:-

// Pin connected to the sensor output
#include "U8glib.h"
U8GLIB_SH1106_128X64 oled(U8G_I2C_OPT_NONE);
const int sensorPin = 2;

// Variables
volatile int rotationCount = 0;
volatile bool rotationDetected = false;
unsigned long prevTime = 0;
float rpm = 0.0;

void setup() {
  // Initialize serial communication
  Serial.begin(9600);

  // Attach an interrupt to the sensor pin
  attachInterrupt(digitalPinToInterrupt(sensorPin), countRotation, FALLING);
}

void loop() {
   oled.firstPage();
  do {
    page1();
  } while (oled.nextPage() ); 
  if (rotationDetected) {
    unsigned long currTime = micros();
    unsigned long timeDiff = currTime - prevTime;

    // Calculate RPM
    rpm = 60000000.0 / timeDiff; // Convert to RPM
    rotationDetected = false;
    prevTime = currTime;
  }
}

void countRotation() {
  // Increment the rotation count
  rotationCount++;
  rotationDetected = true;
}
void page1(void) {
  oled.setFont(u8g_font_profont12);
  oled.setPrintPos(0, 10);
  oled.print("No of rotation : " );

  oled.setPrintPos(0, 25);
  oled.print(rotationCount);
  oled.setPrintPos(0, 40);
  oled.print("RPM of DC Motor :");
  oled.print(rpm);
}

But it has some error, it increases the rpm value as I increase the speed of motor but shows only some fixed values rather than the correct value.

Many proximity sensors are slow, and might not even work for measuring RPM.

Please post a link to whatever it is you have, and explain how it is mounted and what it is supposed to detect.

Post links to your components please, I'm too lazy to look them up myself.

Does your sensor need a Pullup?
Try adding this line
to setup()

pinMode(sensorPin, INPUT_PULLUP);

Proximity Sensor Features/Specs:

  • Model: JG-GI12-04N1
  • Manufacturer: Jigo
  • Material: Metal and plastic
  • Operating Voltage: 6 ~ 36 VDC
  • Output Type: NPN Normally Open
  • Detection object: Metal Objects
  • Detection distance: 4 mm
  • Output Current: 300 mA
  • Total Length of Sensor: 55mm
  • Thread length: 35mm.
  • Outer (Theard) Diameter: M12
  • Polarity protection: yes
  • Surge protection: yes
  • Short circuit protection: yes
  • High repeated positioning accuracy
  • High switching frequency
  • Wide voltage range
  • Antivibration, dust, water, and oil prevention
  • Reverse power protection, short circuit protection, directly connecting with PLC
  • Can replace small switches and limit switches
  • Working temperature(°C): -25 ~ +70, ±15%
  • Cable Length: Approx 1.5 Meter
  • Dimensions: 12 mm Screw Diamete

Motor Specs:-

  • RPM - 200 (i hope, probably higher)
  • Rated Voltage and Current - 12V and 10 amps

Microcontroller:-
Arduino Uno Clone - works just like arduino uno

I have attached external resistors... It works fine the rotation count works perfect, only the rpm measurement has the problem.

Find the attached picture and link of the working video... Notice the rotation count is working fine but the rpm value is changing if fixed values. I changed the speed of the motor by gently adjusting the knob of pwm speed controller.

Working Video:- https://drive.google.com/file/d/1dnwpmWMSZAZI9oEUdOoEYUdjk15OCpV-/view?usp=drive_link

You are computing RPMs based on the time for 1 revolution. That number will vary a little for each revolution.

Try computing RPMs based on the time of 10 revolutions

I don't have a google account so I can't see your video.

1 Like

Access Denied - I've got a google account, but I am asked to "request access".

Try Now!

Will try it.

The fluctions are to be expected.
What that shows is the the motor rotation isn't perfect.
You need to take an average RPM for maybe 10 revolutions

1 Like

I have made few changes to the code.

#include "U8glib.h"
U8GLIB_SH1106_128X64 oled(U8G_I2C_OPT_NONE);
const int sensorPin = 2;

// Variables
volatile int rotationCount = 0;
volatile bool rotationDetected = false;
unsigned long prevTime = 0;
float rpm = 0.0;
float avgRpm = 0.0;
float avgrpm = 0.0;
int count = 0;

void setup() {
  // Initialize serial communication
  Serial.begin(9600);

  // Attach an interrupt to the sensor pin
  attachInterrupt(digitalPinToInterrupt(sensorPin), countRotation, FALLING);
}

void loop() {
   oled.firstPage();
  do {
    page1();
  } while (oled.nextPage() ); 
  if (rotationDetected) {
    
    unsigned long currTime = micros();
    unsigned long timeDiff = currTime - prevTime;

    // Calculate RPM
    rpm = 60000000.0 / timeDiff; // Convert to RPM
    rotationDetected = false;
    prevTime = currTime;
    
    if (count < 10) {
      avgRpm += rpm;
      count++;
    } else {
      avgrpm = avgRpm/10;
      Serial.print("Average RPM: ");
      Serial.println(avgrpm);
      count = 0;
      avgRpm = 0.0;
    }
  }
}

void countRotation() {
  // Increment the rotation count
  rotationCount++;
  rotationDetected = true;
}
void page1(void) {
  oled.setFont(u8g_font_profont12);
  oled.setPrintPos(0, 10);
  oled.print("No of rotation : " );

  oled.setPrintPos(0, 25);
  oled.print(rotationCount);
  oled.setPrintPos(0, 40);
  oled.print("RPM of DC Motor :");
  oled.setPrintPos(0, 55);
  oled.print(avgrpm);
}

It does work to a certain limit, I could get rpm readings in much greater resolution compared to the one obtained using previous code. But I feel like it could still be optimized. Forum, any other suggestions?

You need the best estimate of the actual rpm. Now the RPM will not change instantaneously, so you can improve your estimate based on the history of the readings.
To do this you can use a simple digital filter. An exponential moving average filter is described here

new EMA =F * new reading + (1 -F) * last EMA
where F is a value normally between 0.1 and 0.3

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.